From: mjw@wray-m-3.hpl.hp.com Date: Fri, 25 Jun 2004 15:10:48 +0000 (+0000) Subject: bitkeeper revision 1.1010.1.9 (40dc4078vvvprFxMFMTOULzuaeEQoQ) X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~18098^2~1 X-Git-Url: https://dgit.raspbian.org/%22http:/www.example.com/cgi/%22https:/%22bookmarks://%22Dat/%22http:/www.example.com/cgi/%22https:/%22bookmarks:/%22Dat?a=commitdiff_plain;h=9bdbe09f07b441a13c59e6fcb064aa6ef4b8a4b6;p=xen.git bitkeeper revision 1.1010.1.9 (40dc4078vvvprFxMFMTOULzuaeEQoQ) Renaming 'xenmgr' to 'xen.xend'. --- diff --git a/.rootkeys b/.rootkeys index 2e382934ed..1aabafc723 100644 --- a/.rootkeys +++ b/.rootkeys @@ -198,6 +198,58 @@ 3fbd0a3dTwnDcfdw0-v46dPbX98zDw tools/xc/py/Makefile 3fbd0a42l40lM0IICw2jXbQBVZSdZg tools/xc/py/Xc.c 3fbd0a40yT6G3M9hMpaz5xTUdl0E4g tools/xc/py/setup.py +40c9c468icGyC5RAF1bRKsCXPDCvsA tools/xen/Makefile +40dc4076hGpwa8-sWRN0jtXZeQJuKg tools/xen/lib/__init__.py +40c9c468SNuObE_YWARyS0hzTPSzKg tools/xen/lib/xend/Args.py +40c9c468Um_qc66OQeLEceIz1pgD5g tools/xen/lib/xend/EventServer.py +40c9c468U8EVl0d3G--8YXVg6VJD3g tools/xen/lib/xend/EventTypes.py +40c9c468QJTEuk9g4qHxGpmIi70PEQ tools/xen/lib/xend/PrettyPrint.py +40d8915cyoVA0hJxiBFNymL7YvDaRg tools/xen/lib/xend/XendBridge.py +40c9c4688m3eqnC8fhLu1APm36VOVA tools/xen/lib/xend/XendClient.py +40c9c468t6iIKTjwuYoe-UMCikDcOQ tools/xen/lib/xend/XendConsole.py +40c9c468WnXs6eOUSff23IIGI4kMfQ tools/xen/lib/xend/XendDB.py +40c9c468fSl3H3IypyT0ppkbb0ZT9A tools/xen/lib/xend/XendDomain.py +40c9c468bbKq3uC7_fuNUkiMMjArdw tools/xen/lib/xend/XendDomainConfig.py +40c9c4685ykq87_n1kVUbMr9flx9fg tools/xen/lib/xend/XendDomainInfo.py +40c9c46854nsHmuxHQHncKk5rAs5NA tools/xen/lib/xend/XendMigrate.py +40c9c468M96gA1EYDvNa5w5kQNYLFA tools/xen/lib/xend/XendNode.py +40c9c4686jruMyZIqiaZRMiMoqMJtg tools/xen/lib/xend/XendRoot.py +40c9c468xzANp6o2D_MeCYwNmOIUsQ tools/xen/lib/xend/XendVnet.py +40c9c468x191zetrVlMnExfsQWHxIQ tools/xen/lib/xend/__init__.py +40c9c468S2YnCEKmk4ey8XQIST7INg tools/xen/lib/xend/encode.py +40c9c468DCpMe542varOolW1Xc68ew tools/xen/lib/xend/server/SrvBase.py +40c9c468IxQabrKJSWs0aEjl-27mRQ tools/xen/lib/xend/server/SrvConsole.py +40c9c4689Io5bxfbYIfRiUvsiLX0EQ tools/xen/lib/xend/server/SrvConsoleDir.py +40c9c468woSmBByfeXA4o_jGf2gCgA tools/xen/lib/xend/server/SrvDaemon.py +40c9c468kACsmkqjxBWKHRo071L26w tools/xen/lib/xend/server/SrvDeviceDir.py +40c9c468EQZJVkCLds-OhesJVVyZbQ tools/xen/lib/xend/server/SrvDir.py +40c9c468TyHZUq8sk0FF_vxM6Sozrg tools/xen/lib/xend/server/SrvDomain.py +40c9c469WzajDjutou3X7FmL9hMf3g tools/xen/lib/xend/server/SrvDomainDir.py +40c9c469-8mYEJJTAR6w_ClrJRAfwQ tools/xen/lib/xend/server/SrvEventDir.py +40c9c4694eu5759Dehr4Uhakei0EMg tools/xen/lib/xend/server/SrvNode.py +40c9c469TaZ83ypsrktmPSHLEZiP5w tools/xen/lib/xend/server/SrvRoot.py +40c9c469W3sgDMbBJYQdz5wbQweL0Q tools/xen/lib/xend/server/SrvServer.py +40c9c469aq7oXrE1Ngqf3_lBqL0RoQ tools/xen/lib/xend/server/SrvVnetDir.py +40c9c469Y_aimoOFfUZoS-4eV8gEKg tools/xen/lib/xend/server/__init__.py +40c9c4692hckPol_EK0EGB16ZyDsyQ tools/xen/lib/xend/server/blkif.py +40c9c469N2-b3GqpLHHHPZykJPLVvA tools/xen/lib/xend/server/channel.py +40c9c469hJ_IlatRne-9QEa0-wlquw tools/xen/lib/xend/server/console.py +40c9c469UcNJh_NuLU0ytorM0Lk5Ow tools/xen/lib/xend/server/controller.py +40c9c469vHh-qLiiubdbKEQbJf18Zw tools/xen/lib/xend/server/cstruct.py +40d83983OXjt-y3HjSCcuoPp9rzvmw tools/xen/lib/xend/server/domain.py +40c9c469yrm31i60pGKslTi2Zgpotg tools/xen/lib/xend/server/messages.py +40c9c46925x-Rjb0Cv2f1-l2jZrPYg tools/xen/lib/xend/server/netif.py +40c9c469ZqILEQ8x6yWy0_51jopiCg tools/xen/lib/xend/server/params.py +40c9c469LNxLVizOUpOjEaTKKCm8Aw tools/xen/lib/xend/sxp.py +40d05079aFRp6NQdo5wIh5Ly31c0cg tools/xen/lib/xm/__init__.py +40cf2937gKQcATgXKGtNeWb1PDH5nA tools/xen/lib/xm/create.py +40cf2937isyS250zyd0Q2GuEDoNXfQ tools/xen/lib/xm/main.py +40cf2937PSslwBliN1g7ofDy2H_RhA tools/xen/lib/xm/opts.py +40cf2937Z8WCNOnO2FcWdubvEAF9QQ tools/xen/lib/xm/shutdown.py +40c9c469kT0H9COWzA4XzPBjWK0WsA tools/xen/netfix +40c9c469n2RRwCmjWdjdyyVRWKmgWg tools/xen/setup.py +40c9c4697z76HDfkCLdMhmaEwzFoNQ tools/xen/xend +40cf2937dqM1jWW87O5OoOYND8leuA tools/xen/xm 4055ee41IfFazrwadCH2J72nz-A9YA tools/xenctl/Makefile 4055ee4b_4Rvns_KzE12csI14EKK6Q tools/xenctl/lib/__init__.py 4055ee4dwy4l0MghZosxoiu6zmhc9Q tools/xenctl/lib/console_client.py @@ -205,75 +257,27 @@ 4059c6a0pnxhG8hwSOivXybbGOwuXw tools/xenctl/lib/tempfile.py 3fbd4bd6GtGwZGxYUJPOheYIR7bPaA tools/xenctl/lib/utils.py 4055ee44Bu6oP7U0WxxXypbUt4dNPQ tools/xenctl/setup.py -40431ac64Hj4ixUnKmlugZKhXPFE_Q tools/xend/Makefile -4055ad95Se-FqttgxollqOAAHB94zA tools/xend/lib/__init__.py -4092738fMRGC9fFBcPRCWaJaj9U3ag tools/xend/lib/blkif.py -4055ad97wMLUj0BZT0e_T0EwQN0Bvw tools/xend/lib/console.py -4048c0ddsF0WrU7HUzTvg1MJoCIfWg tools/xend/lib/domain_controller.h -4054a301VEag2GwrBrFBna5U1BGlLA tools/xend/lib/main.py -4055ad9ah9IuC3sJT2c_gYIFY5Tw_g tools/xend/lib/manager.py -409ba2e729HhE7fEra4B5EqX-F8Xzw tools/xend/lib/netif.py -40431ac8wrUEj-XM7B8smFtx_HA7lQ tools/xend/lib/utils.c -4054a2fdkdATEnRw-U7AUlgu-6JiUA tools/xend/setup.py -4056cd26Qyp09iNoOjrvzg8KYzSqOw tools/xend/xend -40c9c468icGyC5RAF1bRKsCXPDCvsA tools/xenmgr/Makefile -40c9c468SNuObE_YWARyS0hzTPSzKg tools/xenmgr/lib/Args.py -40c9c468Um_qc66OQeLEceIz1pgD5g tools/xenmgr/lib/EventServer.py -40c9c468U8EVl0d3G--8YXVg6VJD3g tools/xenmgr/lib/EventTypes.py -40c9c468QJTEuk9g4qHxGpmIi70PEQ tools/xenmgr/lib/PrettyPrint.py -40d8915cyoVA0hJxiBFNymL7YvDaRg tools/xenmgr/lib/XendBridge.py -40c9c4688m3eqnC8fhLu1APm36VOVA tools/xenmgr/lib/XendClient.py -40c9c468t6iIKTjwuYoe-UMCikDcOQ tools/xenmgr/lib/XendConsole.py -40c9c468WnXs6eOUSff23IIGI4kMfQ tools/xenmgr/lib/XendDB.py -40c9c468fSl3H3IypyT0ppkbb0ZT9A tools/xenmgr/lib/XendDomain.py -40c9c468bbKq3uC7_fuNUkiMMjArdw tools/xenmgr/lib/XendDomainConfig.py -40c9c4685ykq87_n1kVUbMr9flx9fg tools/xenmgr/lib/XendDomainInfo.py -40c9c46854nsHmuxHQHncKk5rAs5NA tools/xenmgr/lib/XendMigrate.py -40c9c468M96gA1EYDvNa5w5kQNYLFA tools/xenmgr/lib/XendNode.py -40c9c4686jruMyZIqiaZRMiMoqMJtg tools/xenmgr/lib/XendRoot.py -40c9c468xzANp6o2D_MeCYwNmOIUsQ tools/xenmgr/lib/XendVnet.py -40c9c468x191zetrVlMnExfsQWHxIQ tools/xenmgr/lib/__init__.py -40c9c468S2YnCEKmk4ey8XQIST7INg tools/xenmgr/lib/encode.py -40c9c468DCpMe542varOolW1Xc68ew tools/xenmgr/lib/server/SrvBase.py -40c9c468IxQabrKJSWs0aEjl-27mRQ tools/xenmgr/lib/server/SrvConsole.py -40c9c4689Io5bxfbYIfRiUvsiLX0EQ tools/xenmgr/lib/server/SrvConsoleDir.py -40c9c468woSmBByfeXA4o_jGf2gCgA tools/xenmgr/lib/server/SrvConsoleServer.py -40c9c468kACsmkqjxBWKHRo071L26w tools/xenmgr/lib/server/SrvDeviceDir.py -40c9c468EQZJVkCLds-OhesJVVyZbQ tools/xenmgr/lib/server/SrvDir.py -40c9c468TyHZUq8sk0FF_vxM6Sozrg tools/xenmgr/lib/server/SrvDomain.py -40c9c469WzajDjutou3X7FmL9hMf3g tools/xenmgr/lib/server/SrvDomainDir.py -40c9c469-8mYEJJTAR6w_ClrJRAfwQ tools/xenmgr/lib/server/SrvEventDir.py -40c9c4694eu5759Dehr4Uhakei0EMg tools/xenmgr/lib/server/SrvNode.py -40c9c469TaZ83ypsrktmPSHLEZiP5w tools/xenmgr/lib/server/SrvRoot.py -40c9c469W3sgDMbBJYQdz5wbQweL0Q tools/xenmgr/lib/server/SrvServer.py -40c9c469aq7oXrE1Ngqf3_lBqL0RoQ tools/xenmgr/lib/server/SrvVnetDir.py -40c9c469Y_aimoOFfUZoS-4eV8gEKg tools/xenmgr/lib/server/__init__.py -40c9c4692hckPol_EK0EGB16ZyDsyQ tools/xenmgr/lib/server/blkif.py -40c9c469N2-b3GqpLHHHPZykJPLVvA tools/xenmgr/lib/server/channel.py -40c9c469hJ_IlatRne-9QEa0-wlquw tools/xenmgr/lib/server/console.py -40c9c469UcNJh_NuLU0ytorM0Lk5Ow tools/xenmgr/lib/server/controller.py -40c9c469vHh-qLiiubdbKEQbJf18Zw tools/xenmgr/lib/server/cstruct.py -40d83983OXjt-y3HjSCcuoPp9rzvmw tools/xenmgr/lib/server/domain.py -40c9c469yrm31i60pGKslTi2Zgpotg tools/xenmgr/lib/server/messages.py -40c9c46925x-Rjb0Cv2f1-l2jZrPYg tools/xenmgr/lib/server/netif.py -40c9c469ZqILEQ8x6yWy0_51jopiCg tools/xenmgr/lib/server/params.py -40c9c469LNxLVizOUpOjEaTKKCm8Aw tools/xenmgr/lib/sxp.py -40d05079aFRp6NQdo5wIh5Ly31c0cg tools/xenmgr/lib/xm/__init__.py -40cf2937gKQcATgXKGtNeWb1PDH5nA tools/xenmgr/lib/xm/create.py -40cf2937isyS250zyd0Q2GuEDoNXfQ tools/xenmgr/lib/xm/main.py -40cf2937PSslwBliN1g7ofDy2H_RhA tools/xenmgr/lib/xm/opts.py -40cf2937Z8WCNOnO2FcWdubvEAF9QQ tools/xenmgr/lib/xm/shutdown.py -40c9c469kT0H9COWzA4XzPBjWK0WsA tools/xenmgr/netfix -40c9c469n2RRwCmjWdjdyyVRWKmgWg tools/xenmgr/setup.py -40c9c4697z76HDfkCLdMhmaEwzFoNQ tools/xenmgr/xend -40c9c469JkN47d1oXi-e0RjAP-C6uQ tools/xenmgr/xenmgrd -40cf2937dqM1jWW87O5OoOYND8leuA tools/xenmgr/xm +40431ac64Hj4ixUnKmlugZKhXPFE_Q tools/xend-old/Makefile +4055ad95Se-FqttgxollqOAAHB94zA tools/xend-old/lib/__init__.py +4092738fMRGC9fFBcPRCWaJaj9U3ag tools/xend-old/lib/blkif.py +4055ad97wMLUj0BZT0e_T0EwQN0Bvw tools/xend-old/lib/console.py +4048c0ddsF0WrU7HUzTvg1MJoCIfWg tools/xend-old/lib/domain_controller.h +4054a301VEag2GwrBrFBna5U1BGlLA tools/xend-old/lib/main.py +4055ad9ah9IuC3sJT2c_gYIFY5Tw_g tools/xend-old/lib/manager.py +409ba2e729HhE7fEra4B5EqX-F8Xzw tools/xend-old/lib/netif.py +40431ac8wrUEj-XM7B8smFtx_HA7lQ tools/xend-old/lib/utils.c +4054a2fdkdATEnRw-U7AUlgu-6JiUA tools/xend-old/setup.py +4056cd26Qyp09iNoOjrvzg8KYzSqOw tools/xend-old/xend 403a3edbrr8RE34gkbR40zep98SXbg tools/xentrace/Makefile 40a107afN60pFdURgBv9KwEzgRl5mQ tools/xentrace/formats 4050c413PhhLNAYk3TEwP37i_iLw9Q tools/xentrace/xentrace.8 403a3edbVpV2E_wq1zeEkJ_n4Uu2eg tools/xentrace/xentrace.c 403a3edblCUrzSj0mmKhO5HOPrOrSQ tools/xentrace/xentrace_format 4050c413NtuyIq5lsYJV4P7KIjujXw tools/xentrace/xentrace_format.1 +40dc4076St6AmPTmQPrtQ6LGHPxGmw tools/xu/lib/__init__.py +40dc4076pVeE1kEEWzcUaNZin65kCA tools/xu/lib/domain_controller.h +40dc4076CwBYRTUQDdbdU1L6KcLgSw tools/xu/lib/xu.c +40dc4076FyWUYS2nX9YufgglUzKX2A tools/xu/setup.py 3f72f1bdJPsV3JCnBqs9ddL9tr6D2g xen/COPYING 3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen/Makefile 3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen/Rules.mk diff --git a/extras/mini-os/h/hypervisor.h b/extras/mini-os/h/hypervisor.h index 6009671a4d..c29bd1723e 100644 --- a/extras/mini-os/h/hypervisor.h +++ b/extras/mini-os/h/hypervisor.h @@ -13,7 +13,7 @@ /* include the hypervisor interface */ #include -#include "../../../tools/xend/lib/domain_controller.h" +#include "../../../tools/xu/lib/domain_controller.h" /* * a placeholder for the start of day information passed up from the hypervisor diff --git a/linux-2.4.26-xen-sparse/mkbuildtree b/linux-2.4.26-xen-sparse/mkbuildtree index 54dbaaaca7..19d30ddc0b 100755 --- a/linux-2.4.26-xen-sparse/mkbuildtree +++ b/linux-2.4.26-xen-sparse/mkbuildtree @@ -114,7 +114,7 @@ relative_lndir ../../../${RS}/../xen/include/hypervisor-ifs # Another special symlink: to the shared definitions for the control interface cd .. -ln -sf ../../${RS}/../tools/xend/lib/domain_controller.h +ln -sf ../../${RS}/../tools/xu/lib/domain_controller.h # The remainder are the i386 -> xen-i386 links ln -sf ../asm-i386/a.out.h diff --git a/tools/Makefile b/tools/Makefile index 658ff284d1..69b05709f6 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -1,21 +1,22 @@ all: $(MAKE) -C xc + $(MAKE) -C xu $(MAKE) -C misc $(MAKE) -C examples $(MAKE) -C xentrace $(MAKE) -C xenctl - $(MAKE) -C xend - $(MAKE) -C xenmgr + $(MAKE) -C xen install: all $(MAKE) -C xc install + $(MAKE) -C xu install $(MAKE) -C misc install + $(MAKE) -C xenctl install $(MAKE) -C examples install $(MAKE) -C xentrace install $(MAKE) -C xenctl install - $(MAKE) -C xend install - $(MAKE) -C xenmgr install + $(MAKE) -C xen install dist: $(TARGET) $(MAKE) prefix=`pwd`/../../install dist=yes install @@ -23,10 +24,10 @@ dist: $(TARGET) clean: $(MAKE) -C xc clean + $(MAKE) -C xu clean $(MAKE) -C misc clean $(MAKE) -C examples clean $(MAKE) -C xentrace clean $(MAKE) -C xenctl clean - $(MAKE) -C xend clean - $(MAKE) -C xenmgr clean + $(MAKE) -C xen clean diff --git a/tools/misc/Makefile b/tools/misc/Makefile index 36e9cab93f..69651f2152 100644 --- a/tools/misc/Makefile +++ b/tools/misc/Makefile @@ -3,7 +3,7 @@ CC = gcc CFLAGS = -Wall -O3 EXTRA_INC = -I../../xen/include/hypervisor-ifs EXTRA_INC += -I../../linux-xen-sparse/include -I../xc/lib -EXTRA_INC += -I../xend/lib +EXTRA_INC += -I../xu/lib HDRS = $(wildcard *.h) SRCS = $(wildcard *.c) diff --git a/tools/xc/lib/Makefile b/tools/xc/lib/Makefile index 7a6fd2fa12..271a9c4703 100644 --- a/tools/xc/lib/Makefile +++ b/tools/xc/lib/Makefile @@ -6,7 +6,7 @@ SONAME = libxc.so.$(MAJOR) CC = gcc CFLAGS = -c -Werror -O3 -fno-strict-aliasing CFLAGS += -I../../../xen/include/hypervisor-ifs -CFLAGS += -I../../xend/lib +CFLAGS += -I../../xu/lib CFLAGS += -I../../../linux-xen-sparse/include HDRS = $(wildcard *.h) diff --git a/tools/xc/py/Xc.c b/tools/xc/py/Xc.c index 299202609e..b52114aab2 100644 --- a/tools/xc/py/Xc.c +++ b/tools/xc/py/Xc.c @@ -20,6 +20,8 @@ #define PyMODINIT_FUNC DL_EXPORT(void) #endif +#define XENPKG "xen.ext.xc" + static PyObject *xc_error, *zero; typedef struct { @@ -187,112 +189,73 @@ static PyObject *pyxc_domain_getinfo(PyObject *self, return list; } -static PyObject *pyxc_linux_save(PyObject *self, - PyObject *args, - PyObject *kwds) -{ - XcObject *xc = (XcObject *)self; - - u32 dom; - char *state_file; - int progress = 1, live = -1, debug = 0; - unsigned int flags = 0; - - static char *kwd_list[] = { "dom", "state_file", "progress", - "live", "debug", NULL }; - - if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is|iii", kwd_list, - &dom, &state_file, &progress, - &live, &debug) ) - return NULL; - - if (progress) flags |= XCFLAGS_VERBOSE; - if (live == 1) flags |= XCFLAGS_LIVE; - if (debug) flags |= XCFLAGS_DEBUG; - - if ( strncmp(state_file,"tcp:", strlen("tcp:")) == 0 ) - { +static PyObject *tcp_save(XcObject *xc, u32 dom, char *url, unsigned flags){ #define max_namelen 64 - char server[max_namelen]; - char *port_s; - int port=777; - int sd = -1; - struct hostent *h; - struct sockaddr_in s; - int sockbufsize; - int rc = -1; - - int writerfn(void *fd, const void *buf, size_t count) - { - int tot = 0, rc; - do { - rc = write( (int) fd, ((char*)buf)+tot, count-tot ); - if ( rc < 0 ) { perror("WRITE"); return rc; }; - tot += rc; - } - while ( tot < count ); - return 0; - } - - if (live == -1) flags |= XCFLAGS_LIVE; /* default to live for tcp */ - - strncpy( server, state_file+strlen("tcp://"), max_namelen); - server[max_namelen-1]='\0'; - if ( (port_s = strchr(server,':')) != NULL ) - { - *port_s = '\0'; - port = atoi(port_s+1); + char server[max_namelen]; + char *port_s; + int port=777; + int sd = -1; + struct hostent *h; + struct sockaddr_in s; + int sockbufsize; + int rc = -1; + + int writerfn(void *fd, const void *buf, size_t count) { + int tot = 0, rc; + do { + rc = write( (int) fd, ((char*)buf)+tot, count-tot ); + if ( rc < 0 ) { perror("WRITE"); return rc; }; + tot += rc; } - - printf("X server=%s port=%d\n",server,port); - - h = gethostbyname(server); - sd = socket (AF_INET,SOCK_STREAM,0); - if ( sd < 0 ) - goto serr; - s.sin_family = AF_INET; - bcopy ( h->h_addr, &(s.sin_addr.s_addr), h->h_length); - s.sin_port = htons(port); - if ( connect(sd, (struct sockaddr *) &s, sizeof(s)) ) - goto serr; - - sockbufsize=128*1024; - if ( setsockopt(sd, SOL_SOCKET, SO_SNDBUF, - &sockbufsize, sizeof sockbufsize) < 0 ) - goto serr; - - if ( xc_linux_save(xc->xc_handle, dom, flags, - writerfn, (void*)sd) == 0 ) - { - if ( read( sd, &rc, sizeof(int) ) != sizeof(int) ) - goto serr; + while ( tot < count ); + return 0; + } + + strncpy( server, url+strlen("tcp://"), max_namelen); + server[max_namelen-1]='\0'; + if ( (port_s = strchr(server,':')) != NULL ) { + *port_s = '\0'; + port = atoi(port_s+1); + } + printf("X server=%s port=%d\n",server,port); + h = gethostbyname(server); + sd = socket(AF_INET, SOCK_STREAM,0); + if (sd < 0) goto serr; + s.sin_family = AF_INET; + bcopy ( h->h_addr, &(s.sin_addr.s_addr), h->h_length); + s.sin_port = htons(port); + if ( connect(sd, (struct sockaddr *) &s, sizeof(s)) ) goto serr; + sockbufsize=128*1024; + if ( setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, sizeof sockbufsize) < 0 ) goto serr; + + if ( xc_linux_save(xc->xc_handle, dom, flags, writerfn, (void*)sd) == 0 ) { + if ( read( sd, &rc, sizeof(int) ) != sizeof(int) ) goto serr; - if ( rc == 0 ) - { + if ( rc == 0 ) { printf("Migration succesful -- destroy local copy\n"); xc_domain_destroy(xc->xc_handle, dom); close(sd); Py_INCREF(zero); return zero; - } - else - errno = rc; + } else { + errno = rc; } + } - serr: - printf("Migration failed -- restart local copy\n"); - xc_domain_unpause(xc->xc_handle, dom); - PyErr_SetFromErrno(xc_error); - if ( sd >= 0 ) close(sd); - return NULL; - } - else - { + serr: + printf("Migration failed -- restart local copy\n"); + xc_domain_unpause(xc->xc_handle, dom); + PyErr_SetFromErrno(xc_error); + if ( sd >= 0 ) close(sd); + return NULL; + +} + +static PyObject *file_save(XcObject *xc, u32 dom, char *state_file, unsigned flags){ int fd = -1; gzFile gfd = NULL; - int writerfn(void *fd, const void *buf, size_t count) - { + int writerfn(void *fd, const void *buf, size_t count) { int rc; while ( ((rc = gzwrite( (gzFile*)fd, (void*)buf, count)) == -1) && (errno = EINTR) ) @@ -300,30 +263,24 @@ static PyObject *pyxc_linux_save(PyObject *self, return ! (rc == count); } - if (strncmp(state_file,"file:",strlen("file:")) == 0) + if (strncmp(state_file,"file:",strlen("file:")) == 0){ state_file += strlen("file:"); + } - if ( (fd = open(state_file, O_CREAT|O_EXCL|O_WRONLY, 0644)) == -1 ) - { + if ( (fd = open(state_file, O_CREAT|O_EXCL|O_WRONLY, 0644)) == -1 ) { perror("Could not open file for writing"); goto err; } - /* * Compression rate 1: we want speed over compression. * We're mainly going for those zero pages, after all. */ - - if ( (gfd = gzdopen(fd, "wb1")) == NULL ) - { + if ( (gfd = gzdopen(fd, "wb1")) == NULL ) { perror("Could not allocate compression state for state file"); close(fd); goto err; } - - - if ( xc_linux_save(xc->xc_handle, dom, flags, writerfn, gfd) == 0 ) - { + if ( xc_linux_save(xc->xc_handle, dom, flags, writerfn, gfd) == 0 ) { /* kill domain. We don't want to do this for checkpointing, but if we don't do it here I think people will hurt themselves by accident... */ @@ -337,14 +294,45 @@ static PyObject *pyxc_linux_save(PyObject *self, err: PyErr_SetFromErrno(xc_error); - if ( gfd != NULL ) - gzclose(gfd); - if ( fd >= 0 ) - close(fd); + if ( gfd != NULL ) gzclose(gfd); + if ( fd >= 0 ) close(fd); unlink(state_file); return NULL; - } +} + +static PyObject *pyxc_linux_save(PyObject *self, + PyObject *args, + PyObject *kwds) +{ + XcObject *xc = (XcObject *)self; + u32 dom; + char *state_file; + int progress = 1, live = -1, debug = 0; + unsigned int flags = 0; + PyObject *val = NULL; + + static char *kwd_list[] = { "dom", "state_file", "progress", + "live", "debug", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is|iii", kwd_list, + &dom, &state_file, &progress, + &live, &debug) ) + goto exit; + + if (progress) flags |= XCFLAGS_VERBOSE; + if (live == 1) flags |= XCFLAGS_LIVE; + if (debug) flags |= XCFLAGS_DEBUG; + + if ( strncmp(state_file,"tcp:", strlen("tcp:")) == 0 ) { + /* default to live for tcp */ + if (live == -1) flags |= XCFLAGS_LIVE; + val = tcp_save(xc, dom, state_file, flags); + } else { + val = file_save(xc, dom, state_file, flags); + } + exit: + return val; } static PyObject *pyxc_linux_restore(PyObject *self, @@ -1335,18 +1323,18 @@ static PyTypeObject PyXcType = { }; static PyMethodDef PyXc_methods[] = { - { "new", PyXc_new, METH_VARARGS, "Create a new Xc object." }, + { "new", PyXc_new, METH_VARARGS, "Create a new " XENPKG " object." }, { NULL, NULL, 0, NULL } }; -PyMODINIT_FUNC initXc(void) +PyMODINIT_FUNC initxc(void) { PyObject *m, *d; - m = Py_InitModule("Xc", PyXc_methods); + m = Py_InitModule(XENPKG, PyXc_methods); d = PyModule_GetDict(m); - xc_error = PyErr_NewException("Xc.error", NULL, NULL); + xc_error = PyErr_NewException(XENPKG ".error", NULL, NULL); PyDict_SetItemString(d, "error", xc_error); zero = PyInt_FromLong(0); diff --git a/tools/xc/py/setup.py b/tools/xc/py/setup.py index 84eb6239a2..b295ed295b 100644 --- a/tools/xc/py/setup.py +++ b/tools/xc/py/setup.py @@ -1,11 +1,14 @@ from distutils.core import setup, Extension -module = Extension("Xc", +module = Extension("xc", extra_compile_args = ["-fno-strict-aliasing"], include_dirs = ["../lib"], library_dirs = ["../lib"], libraries = ["xc"], sources = ["Xc.c"]) -setup(name = "Xc", version = "1.0", ext_modules = [module]) +setup(name = "xc", + version = "1.0", + ext_package = "xen.ext", + ext_modules = [module]) diff --git a/tools/xen/Makefile b/tools/xen/Makefile new file mode 100644 index 0000000000..642cd2273e --- /dev/null +++ b/tools/xen/Makefile @@ -0,0 +1,19 @@ + +all: + python setup.py build + +install: all + if [ "$(prefix)" = "" ]; then \ + python setup.py install; \ + elif [ "$(dist)" = "yes" ]; then \ + python setup.py install --home="$(prefix)"; \ + else \ + python setup.py install --root="$(prefix)"; \ + fi + mkdir -p $(prefix)/usr/sbin + install -m0755 xend $(prefix)/usr/sbin + install -m0755 netfix $(prefix)/usr/sbin + install -m0755 xm $(prefix)/usr/sbin + +clean: + rm -rf build *.pyc *.pyo *.o *.a *~ diff --git a/tools/xen/lib/__init__.py b/tools/xen/lib/__init__.py new file mode 100644 index 0000000000..8d1c8b69c3 --- /dev/null +++ b/tools/xen/lib/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/xen/lib/xend/Args.py b/tools/xen/lib/xend/Args.py new file mode 100644 index 0000000000..527e841d3d --- /dev/null +++ b/tools/xen/lib/xend/Args.py @@ -0,0 +1,126 @@ +import sxp + +class ArgError(StandardError): + pass + +class Args: + """Argument encoding support for HTTP. + """ + + def __init__(self, paramspec, keyspec): + self.arg_ord = [] + self.arg_dict = {} + self.key_ord = [] + self.key_dict = {} + for (name, type) in paramspec: + self.arg_ord.append(name) + self.arg_dict[name] = type + for (name, type) in keyspec: + self.key_ord.append(name) + self.key_dict[name] = type + + def get_args(self, d, xargs=None): + args = {} + keys = {} + params = [] + if xargs: + self.split_args(xargs, args, keys) + self.split_args(d, args, keys) + for a in self.arg_ord: + if a in args: + params.append(args[a]) + else: + raise ArgError('Missing parameter: %s' % a) + return (params, keys) + + def split_args(self, d, args, keys): + for (k, v) in d.items(): + if k in self.arg_dict: + type = self.arg_dict[k] + val = self.coerce(type, v) + args[k] = val + elif k in self.key_dict: + type = self.key_dict[k] + val = self.coerce(type, v) + keys[k] = val + else: + raise ArgError('Invalid parameter: %s' % k) + + def get_form_args(self, f, xargs=None): + d = {} + for (k, v) in f.items(): + n = len(v) + if ((k not in self.arg_dict) and + (k not in self.key_dict)): + continue + if n == 0: + continue + elif n == 1: + d[k] = v[0] + else: + raise ArgError('Too many values for %s' % k) + return self.get_args(d, xargs=xargs) + + def coerce(self, type, v): + try: + if type == 'int': + return int(v) + if type == 'str': + return str(v) + if type == 'sxpr': + return self.sxpr(v) + except ArgError: + raise + except StandardError, ex: + raise ArgError(str(ex)) + + def sxpr(self, v): + if instanceof(v, types.ListType): + return v + if instanceof(v, types.File) or hasattr(v, 'readline'): + return sxpr_file(v) + if instanceof(v, types.StringType): + return sxpr_file(StringIO(v)) + return str(v) + + def sxpr_file(self, fin): + try: + vals = sxp.parse(fin) + except: + raise ArgError('Coercion to sxpr failed') + if len(vals) == 1: + return vals[0] + else: + raise ArgError('Too many sxprs') + + def call_with_args(self, fn, args, xargs=None): + (params, keys) = self.get_args(args, xargs=xargs) + fn(*params, **keys) + + def call_with_form_args(self, fn, fargs, xargs=None): + (params, keys) = self.get_form_args(fargs, xargs=xargs) + fn(*params, **keys) + +class ArgFn(Args): + """Represent a remote HTTP operation as a function. + Used on the client. + """ + + def __init__(self, fn, paramspec, keyspec={}): + Args.__init__(self, paramspec, keyspec) + self.fn = fn + + def __call__(self, fargs, xargs=None): + return self.call_with_args(self.fn, fargs, xargs=xargs) + +class FormFn(Args): + """Represent an operation as a function over a form. + Used in the HTTP server. + """ + + def __init__(self, fn, paramspec, keyspec={}): + Args.__init__(self, paramspec, keyspec) + self.fn = fn + + def __call__(self, fargs, xargs=None): + return self.call_with_form_args(self.fn, fargs, xargs=xargs) diff --git a/tools/xen/lib/xend/EventServer.py b/tools/xen/lib/xend/EventServer.py new file mode 100644 index 0000000000..20c567ada7 --- /dev/null +++ b/tools/xen/lib/xend/EventServer.py @@ -0,0 +1,215 @@ +# Copyright (C) 2004 Mike Wray +"""Simple publish/subscribe event server. + +""" +import string + +from twisted.internet import reactor + +# subscribe a.b.c h: map a.b.c -> h +# subscribe a.b.* h: map a.b.* -> h +# subscribe a.b.? h: map a.b.? -> h +# +# for event a.b.c.d: +# +# lookup a.b.c.d, call handlers +# +# lookup a.b.c.?, call handlers +# +# lookup a.b.c.d.*, call handlers +# lookup a.b.c.*, call handlers +# lookup a.b.*, call handlers +# lookup a.*, call handlers +# lookup *, call handlers + +# a.b.c.d = (a b c d) +# a.b.c.? = (a b c _) +# a.b.c.* = (a b c . _) + +class EventServer: + + DOT = '.' + QUERY = '?' + DOT_QUERY = DOT + QUERY + STAR = '*' + DOT_STAR = DOT + STAR + + def __init__(self, run=0): + self.handlers = {} + self.run = run + self.queue = [] + + def start(self): + """Enable event handling. Sends any queued events. + """ + self.run = 1 + for (e,v) in self.queue: + self.inject(e, v) + self.queue = [] + + def stop(self): + """Suspend event handling. Events injected while suspended + are queued until we are started again. + """ + self.run = 0 + + def subscribe(self, event, handler): + """Subscribe to an event. For example 'a.b.c.d'. + A subcription like 'a.b.c.?' ending in '?' matches any value + for the '?'. A subscription like 'a.b.c.*' ending in '*' matches + any event type with the same prefix, 'a.b.c' in this case. + + event event name + handler event handler fn(event, val) + """ + hl = self.handlers.get(event) + if hl is None: + self.handlers[event] = [handler] + else: + hl.append(handler) + + def unsubscribe_all(self, event=None): + """Unsubscribe all handlers for a given event, or all handlers. + + event event (optional) + """ + if event == None: + self.handlers.clear() + elif event in self.handlers: + del self.handlers[event] + + def unsubscribe(self, event, handler): + """Unsubscribe a given event and handler. + + event event + handler handler + """ + hl = self.handlers.get(event) + if hl is None: + return + if handler in hl: + hl.remove(handler) + + def inject(self, event, val, async=1): + """Inject an event. Handlers for it are called if running, otherwise + it is queued. + + event event type + val event value + """ + if self.run: + if async: + reactor.callLater(0, self.call_handlers, event, val) + else: + self.notify_handlers(event, val) + else: + self.queue.append( (event, val) ) + + def call_handlers(self, event, val): + """Internal method to call event handlers. + """ + #print ">event", event, val + self.call_event_handlers(event, event, val) + self.call_query_handlers(event, val) + self.call_star_handlers(event, val) + + def call_event_handlers(self, key, event, val): + """Call the handlers for an event. + It is safe for handlers to subscribe or unsubscribe. + + key key for handler list + event event type + val event value + """ + hl = self.handlers.get(key) + if hl is None: + return + # Copy the handler list so that handlers can call + # subscribe/unsubscribe safely - python list iteration + # is not safe against list modification. + for h in hl[:]: + try: + h(event, val) + except: + pass + + def call_query_handlers(self, event, val): + """Call regex handlers for events matching 'event' that end in '?'. + + event event type + val event value + """ + dot_idx = event.rfind(self.DOT) + if dot_idx == -1: + self.call_event_handlers(self.QUERY, event, val) + else: + event_query = event[0:dot_idx] + self.DOT_QUERY + self.call_event_handlers(event_query, event, val) + + def call_star_handlers(self, event, val): + """Call regex handlers for events matching 'event' that end in '*'. + + event event type + val event value + """ + etype = string.split(event, self.DOT) + for i in range(len(etype), 0, -1): + event_star = self.DOT.join(etype[0:i]) + self.DOT_STAR + self.call_event_handlers(event_star, event, val) + self.call_event_handlers(self.STAR, event, val) + +def instance(): + global inst + try: + inst + except: + inst = EventServer() + inst.start() + return inst + +def main(): + def sys_star(event, val): + print 'sys_star', event, val + + def sys_foo(event, val): + print 'sys_foo', event, val + s.unsubscribe('sys.foo', sys_foo) + + def sys_foo2(event, val): + print 'sys_foo2', event, val + + def sys_bar(event, val): + print 'sys_bar', event, val + + def sys_foo_bar(event, val): + print 'sys_foo_bar', event, val + + def foo_bar(event, val): + print 'foo_bar', event, val + + s = EventServer() + s.start() + s.subscribe('sys.*', sys_star) + s.subscribe('sys.foo', sys_foo) + s.subscribe('sys.foo', sys_foo2) + s.subscribe('sys.bar', sys_bar) + s.subscribe('sys.foo.bar', sys_foo_bar) + s.subscribe('foo.bar', foo_bar) + s.inject('sys.foo', 'hello') + print + s.inject('sys.bar', 'hello again') + print + s.inject('sys.foo.bar', 'hello again') + print + s.inject('foo.bar', 'hello again') + print + s.inject('foo', 'hello again') + print + s.start() + s.unsubscribe('sys.*', sys_star) + s.unsubscribe_all('sys.*') + s.inject('sys.foo', 'hello') + +if __name__ == "__main__": + main() + diff --git a/tools/xen/lib/xend/EventTypes.py b/tools/xen/lib/xend/EventTypes.py new file mode 100644 index 0000000000..6350baa5dd --- /dev/null +++ b/tools/xen/lib/xend/EventTypes.py @@ -0,0 +1,34 @@ +# Copyright (C) 2004 Mike Wray + +## XEND_DOMAIN_CREATE = "xend.domain.create": dom +## create: +## xend.domain.destroy: dom, reason:died/crashed +## xend.domain.up ? + +## xend.domain.unpause: dom +## xend.domain.pause: dom +## xend.domain.shutdown: dom +## xend.domain.destroy: dom + +## xend.domain.migrate.begin: dom, to +## Begin tells: src host, src domain uri, dst host. Dst id known? +## err: src host, src domain uri, dst host, dst id if known, status (of domain: ok, dead,...), reason +## end: src host, src domain uri, dst host, dst uri + +## Events for both ends of migrate: for exporter and importer? +## Include migrate id so can tie together. +## Have uri /xend/migrate/ for migrate info (migrations in progress). + +## (xend.domain.migrate.begin (src ) (src.domain ) +## (dst ) (id )) + +## xend.domain.migrate.end: +## (xend.domain.migrate.end (domain ) (to ) + +## xend.node.up: xend uri +## xend.node.down: xend uri + +## xend.error ? + +## format: + diff --git a/tools/xen/lib/xend/PrettyPrint.py b/tools/xen/lib/xend/PrettyPrint.py new file mode 100644 index 0000000000..9e91b11448 --- /dev/null +++ b/tools/xen/lib/xend/PrettyPrint.py @@ -0,0 +1,299 @@ +# Copyright (C) 2004 Mike Wray + +"""General pretty-printer, including support for SXP. + +""" +import sys +import types +import StringIO +import sxp + +class PrettyItem: + + def __init__(self, width): + self.width = width + + def insert(self, block): + block.addtoline(self) + + def get_width(self): + return self.width + + def output(self, out): + print '***PrettyItem>output>', self + pass + + def prettyprint(self, out, width): + print '***PrettyItem>prettyprint>', self + return width + +class PrettyString(PrettyItem): + + def __init__(self, x): + PrettyItem.__init__(self, len(x)) + self.value = x + + def output(self, out): + out.write(self.value) + + def prettyprint(self, line): + line.output(self) + + def show(self, out): + print >> out, ("(string (width %d) '%s')" % (self.width, self.value)) + +class PrettySpace(PrettyItem): + + def output(self, out): + out.write(' ' * self.width) + + def prettyprint(self, line): + line.output(self) + + def show(self, out): + print >> out, ("(space (width %d))" % self.width) + +class PrettyBreak(PrettyItem): + + def __init__(self, width, indent): + PrettyItem.__init__(self, width) + self.indent = indent + self.space = 0 + self.active = 0 + + def output(self, out): + out.write(' ' * self.width) + + def prettyprint(self, line): + if line.breaks(self.space): + self.active = 1 + line.newline(self.indent) + else: + line.output(self) + + def show(self, out): + print >> out, ("(break (width %d) (indent %d) (space %d) (active %d))" + % (self.width, self.indent, self.space, self.lspace, self.active)) + +class PrettyNewline(PrettySpace): + + def __init__(self, indent): + PrettySpace.__init__(self, indent) + + def insert(self, block): + block.newline() + block.addtoline(self) + + def output(self, out): + out.write(' ' * self.width) + + def prettyprint(self, line): + line.newline(0) + line.output(self) + + def show(self, out): + print >> out, ("(nl (indent %d))" % self.indent) + +class PrettyLine(PrettyItem): + def __init__(self): + PrettyItem.__init__(self, 0) + self.content = [] + + def write(self, x): + self.content.append(x) + + def end(self): + width = 0 + lastwidth = 0 + lastbreak = None + for x in self.content: + if isinstance(x, PrettyBreak): + if lastbreak: + lastbreak.space = (width - lastwidth) + lastbreak = x + lastwidth = width + width += x.get_width() + if lastbreak: + lastbreak.space = (width - lastwidth) + self.width = width + + def prettyprint(self, line): + for x in self.content: + x.prettyprint(line) + + def show(self, out): + print >> out, '(LINE (width %d)' % self.width + for x in self.content: + x.show(out) + print >> out, ')' + +class PrettyBlock(PrettyItem): + + def __init__(self, all=0, parent=None): + self.width = 0 + self.lines = [] + self.parent = parent + self.indent = 0 + self.all = all + self.broken = 0 + self.newline() + + def add(self, item): + item.insert(self) + + def end(self): + self.width = 0 + for l in self.lines: + l.end() + if self.width < l.width: + self.width = l.width + + def breaks(self, n): + return self.all and self.broken + + def newline(self): + self.lines.append(PrettyLine()) + + def addtoline(self, x): + self.lines[-1].write(x) + + def prettyprint(self, line): + self.indent = line.used + line.block = self + if not line.fits(self.width): + self.broken = 1 + for l in self.lines: + l.prettyprint(line) + line.block = self.parent + + def show(self, out): + print >> out, ('(BLOCK (width %d) (indent %d) (all %d) (broken %d)' % + (self.width, self.indent, self.all, self.broken)) + for l in self.lines: + l.show(out) + print >> out, ')' + +class Line: + + def __init__(self, out, width): + self.out = out + self.width = width + self.used = 0 + self.space = self.width + + def newline(self, indent): + indent += self.block.indent + self.out.write('\n') + self.out.write(' ' * indent) + self.used = indent + self.space = self.width - self.used + + def fits(self, n): + return self.space - n >= 0 + + def breaks(self, n): + return self.block.breaks(n) or not self.fits(n) + + def output(self, x): + n = x.get_width() + self.space -= n + self.used += n + if self.space < 0: + self.space = 0 + x.output(self.out) + +class PrettyPrinter: + """A prettyprinter based on what I remember of Derek Oppen's + prettyprint algorithm from TOPLAS way back. + """ + + def __init__(self, width=40): + self.width = width + self.block = None + self.top = None + + def write(self, x): + self.block.add(PrettyString(x)) + + def add(self, item): + self.block.add(item) + + def addbreak(self, width=1, indent=4): + self.add(PrettyBreak(width, indent)) + + def addspace(self, width=1): + self.add(PrettySpace(width)) + + def addnl(self, indent=0): + self.add(PrettyNewline(indent)) + + def begin(self, all=0): + block = PrettyBlock(all=all, parent=self.block) + self.block = block + + def end(self): + self.block.end() + if self.block.parent: + self.block.parent.add(self.block) + else: + self.top = self.block + self.block = self.block.parent + + def prettyprint(self, out=sys.stdout): + line = Line(out, self.width) + self.top.prettyprint(line) + +class SXPPrettyPrinter(PrettyPrinter): + """An SXP prettyprinter. + """ + + def pstring(self, x): + io = StringIO.StringIO() + sxp.show(x, out=io) + io.seek(0) + val = io.getvalue() + io.close() + return val + + def pprint(self, l): + if isinstance(l, types.ListType): + self.begin(all=1) + self.write('(') + i = 0 + for x in l: + if(i): self.addbreak() + self.pprint(x) + i += 1 + self.addbreak(width=0, indent=0) + self.write(')') + self.end() + else: + self.write(self.pstring(l)) + +def prettyprint(sxpr, out=sys.stdout, width=80): + """Prettyprint an SXP form. + + sxpr s-expression + out destination + width maximum output width + """ + if isinstance(sxpr, types.ListType): + pp = SXPPrettyPrinter(width=width) + pp.pprint(sxpr) + pp.prettyprint(out=out) + else: + sxp.show(sxpr, out=out) + print >> out + +def main(): + pin = sxp.Parser() + while 1: + buf = sys.stdin.read(100) + pin.input(buf) + if buf == '': break + l = pin.get_val() + prettyprint(l, width=80) + +if __name__ == "__main__": + main() + diff --git a/tools/xen/lib/xend/XendBridge.py b/tools/xen/lib/xend/XendBridge.py new file mode 100644 index 0000000000..f1584552de --- /dev/null +++ b/tools/xen/lib/xend/XendBridge.py @@ -0,0 +1,193 @@ +"""Bridge control utilities. +""" +import os +import os.path +import re +import sys + +from xen.xend import XendRoot +xroot = XendRoot.instance() + +os.defpath = os.defpath + ':/sbin:/usr/sbin:/usr/local/sbin' +CMD_IFCONFIG = 'ifconfig' +CMD_ROUTE = 'route' +CMD_BRCTL = 'brctl' +CMD_IPTABLES = "iptables" + +DEFAULT_BRIDGE = 'nbe-br' +DEFAULT_INTERFACE = 'eth0' + +opts = None + +class Opts: + + def __init__(self, defaults): + for (k, v) in defaults.items(): + setattr(self, k, v) + pass + +def cmd(p, s): + """Print and execute command 'p' with args 's'. + """ + global opts + c = p + ' ' + s + if opts.verbose: print c + if not opts.dryrun: + os.system(c) + +def default_bridge(): + return xroot.get_config_value('bridge', DEFAULT_BRIDGE) + +def default_interface(): + return xroot.get_config_value('interface', DEFAULT_INTERFACE) + +def vif_dev(dom, vif): + """Return the name of the network interface for vif on domain dom. + """ + return "vif%d.%d" % (dom, vif) + +def vif_bridge_add(dom, vif, bridge=None): + """Add the network interface for vif on dom to a bridge. + """ + if not bridge: bridge = default_bridge() + d = { 'bridge': bridge, 'vif': vif_dev(dom, vif) } + cmd(CMD_BRCTL, 'addif %(bridge)s %(vif)s' % d) + return bridge + +def vif_bridge_rem(dom, vif, bridge=None): + """Remove the network interface for vif on dom from a bridge. + """ + if not bridge: bridge = default_bridge() + print 'vif_bridge_rem>', dom, vif, bridge + d = { 'bridge': bridge, 'vif': vif_dev(dom, vif) } + cmd(CMD_BRCTL, 'delif %(bridge)s %(vif)s' % d) + +def vif_restrict_addr(dom, vif, addr, delete=0): + d = { 'vif': vif_dev(dom, vif), 'addr': addr} + if delete: + d['flag'] = '-D' + else: + d['flag'] = '-A' + cmd(CMD_IPTABLES, '-P FORWARD DROP') + cmd(CMD_IPTABLES, '%(flag)s FORWARD -m physdev --physdev-in %(vif)s -s %(addr)s -j ACCEPT' % d) + cmd(CMD_IPTABLES, '%(flag)s FORWARD -m physdev --physdev-out %(vif)s -d %(addr)s -j ACCEPT' % d) + +def bridge_create(bridge=None, **kwd): + """Create a bridge. + Defaults hello time to 0, forward delay to 0 and stp off. + """ + if not bridge: bridge = default_bridge() + cmd(CMD_BRCTL, 'addbr %s' % bridge) + if kwd.get('hello', None) is None: + kwd['hello'] = 0 + if kwd.get('fd', None) is None: + kwd['fd'] = 0 + if kwd.get('stp', None) is None: + kwd['stp'] = 'off' + bridge_set(bridge, **kwd) + +def bridge_set(bridge, hello=None, fd=None, stp=None): + """Set bridge parameters. + """ + if hello is not None: + cmd(CMD_BRCTL, 'sethello %s %d' % (bridge, hello)) + if fd is not None: + cmd(CMD_BRCTL, 'setfd %s %d' % (bridge, fd)) + if stp is not None: + cmd(CMD_BRCTL, 'stp %s %s' % (bridge, stp)) + +def bridge_del(bridge=None): + """Delete a bridge. + """ + if not bridge: bridge = default_bridge() + cmd(CMD_BRCTL, 'delbr %s' % bridge) + +def routes(): + """Return a list of the routes. + """ + fin = os.popen(CMD_ROUTE + ' -n', 'r') + routes = [] + for x in fin: + if x.startswith('Kernel'): continue + if x.startswith('Destination'): continue + x = x.strip() + y = x.split() + z = { 'destination': y[0], + 'gateway' : y[1], + 'mask' : y[2], + 'flags' : y[3], + 'metric' : y[4], + 'ref' : y[5], + 'use' : y[6], + 'interface' : y[7] } + routes.append(z) + return routes + +def ifconfig(interface): + """Return the ip config for an interface, + """ + fin = os.popen(CMD_IFCONFIG + ' %s' % interface, 'r') + inetre = re.compile('\s*inet\s*addr:(?P
\S*)\s*Bcast:(?P\S*)\s*Mask:(?P\S*)') + info = None + for x in fin: + m = inetre.match(x) + if not m: continue + info = m.groupdict() + info['interface'] = interface + break + return info + +def reconfigure(interface=None, bridge=None): + """Reconfigure an interface to be attached to a bridge, and give the bridge + the IP address etc. from interface. Move the default route to the interface + to the bridge. + + If opts.create is true, creates the bridge. + """ + global opts + if not interface: interface = default_interface() + if not bridge: bridge = default_bridge() + intf_info = ifconfig(interface) + if not intf_info: + print 'Interface not found:', interface + return + if opts.create: + bridge_create(bridge) + #bridge_info = ifconfig(bridge) + #if not bridge_info: + # print 'Bridge not found:', bridge + # return + route_info = routes() + intf_info['bridge'] = bridge + intf_info['gateway'] = None + for r in route_info: + if (r['destination'] == '0.0.0.0' and + 'G' in r['flags'] and + r['interface'] == interface): + intf_info['gateway'] = r['gateway'] + if not intf_info['gateway']: + print 'Gateway not found: ', interface + return + cmd(CMD_IFCONFIG, + '%(bridge)s %(address)s netmask %(mask)s broadcast %(broadcast)s up' + % intf_info) + cmd(CMD_ROUTE, + 'add default gateway %(gateway)s dev %(bridge)s' + % intf_info) + cmd(CMD_BRCTL, 'addif %(bridge)s %(interface)s' % intf_info) + cmd(CMD_IFCONFIG, '%(interface)s 0.0.0.0' % intf_info) + +defaults = { + 'interface': default_interface(), + 'bridge' : default_bridge(), + 'verbose' : 1, + 'dryrun' : 0, + 'create' : 0, + } + +opts = Opts(defaults) + +def set_opts(val): + global opts + opts = val + return opts diff --git a/tools/xen/lib/xend/XendClient.py b/tools/xen/lib/xend/XendClient.py new file mode 100644 index 0000000000..13dc3dbb1e --- /dev/null +++ b/tools/xen/lib/xend/XendClient.py @@ -0,0 +1,311 @@ +# Copyright (C) 2004 Mike Wray +"""Client API for the HTTP interface on xend. +Callable as a script - see main(). +""" +import sys +import httplib +import types +from StringIO import StringIO +import urlparse + +from encode import * +import sxp +import PrettyPrint + +DEBUG = 0 + +class Foo(httplib.HTTPResponse): + + def begin(self): + fin = self.fp + while(1): + buf = fin.readline() + print "***", buf + if buf == '': + print + sys.exit() + + +def sxprio(sxpr): + io = StringIO() + sxp.show(sxpr, out=io) + print >> io + io.seek(0) + return io + +def fileof(val): + """Converter for passing configs. + Handles lists, files directly. + Assumes a string is a file name and passes its contents. + """ + if isinstance(val, types.ListType): + return sxprio(val) + if isinstance(val, types.StringType): + return file(val) + if hasattr(val, 'readlines'): + return val + +# todo: need to sort of what urls/paths are using for objects. +# e.g. for domains at the moment return '0'. +# should probably return abs path w.r.t. server, e.g. /xend/domain/0. +# As an arg, assume abs path is obj uri, otherwise just id. + +# Function to convert to full url: Xend.uri(path), e.g. +# maps /xend/domain/0 to http://wray-m-3.hpl.hp.com:8000/xend/domain/0 +# And should accept urls for ids? + +def urljoin(location, root, prefix='', rest=''): + prefix = str(prefix) + rest = str(rest) + base = 'http://' + location + root + prefix + url = urlparse.urljoin(base, rest) + return url + +def nodeurl(location, root, id=''): + return urljoin(location, root, 'node/', id) + +def domainurl(location, root, id=''): + return urljoin(location, root, 'domain/', id) + +def consoleurl(location, root, id=''): + return urljoin(location, root, 'console/', id) + +def deviceurl(location, root, id=''): + return urljoin(location, root, 'device/', id) + +def vneturl(location, root, id=''): + return urljoin(location, root, 'vnet/', id) + +def eventurl(location, root, id=''): + return urljoin(location, root, 'event/', id) + +def xend_request(url, method, data=None): + urlinfo = urlparse.urlparse(url) + (uproto, ulocation, upath, uparam, uquery, ufrag) = urlinfo + if DEBUG: print url, urlinfo + if uproto != 'http': + raise StandardError('Invalid protocol: ' + uproto) + if DEBUG: print '>xend_request', ulocation, upath, method, data + (hdr, args) = encode_data(data) + if data and method == 'GET': + upath += '?' + args + args = None + if method == "POST" and upath.endswith('/'): + upath = upath[:-1] + if DEBUG: print "ulocation=", ulocation, "upath=", upath, "args=", args + #hdr['User-Agent'] = 'Mozilla' + #hdr['Accept'] = 'text/html,text/plain' + conn = httplib.HTTPConnection(ulocation) + #conn.response_class = Foo + if DEBUG: conn.set_debuglevel(1) + conn.request(method, upath, args, hdr) + resp = conn.getresponse() + if DEBUG: print resp.status, resp.reason + if DEBUG: print resp.msg.headers + if resp.status in [204, 404]: + return None + if resp.status not in [200, 201, 202, 203]: + raise RuntimeError(resp.reason) + pin = sxp.Parser() + data = resp.read() + if DEBUG: print "***data" , data + if DEBUG: print "***" + pin.input(data); + pin.input_eof() + conn.close() + val = pin.get_val() + #if isinstance(val, types.ListType) and sxp.name(val) == 'val': + # val = val[1] + if isinstance(val, types.ListType) and sxp.name(val) == 'err': + raise RuntimeError(val[1]) + if DEBUG: print '**val='; sxp.show(val); print + return val + +def xend_get(url, args=None): + return xend_request(url, "GET", args) + +def xend_call(url, data): + return xend_request(url, "POST", data) + +class Xend: + + SRV_DEFAULT = "localhost:8000" + ROOT_DEFAULT = "/xend/" + + def __init__(self, srv=None, root=None): + self.bind(srv, root) + + def bind(self, srv=None, root=None): + if srv is None: srv = self.SRV_DEFAULT + if root is None: root = self.ROOT_DEFAULT + if not root.endswith('/'): root += '/' + self.location = srv + self.root = root + + def nodeurl(self, id=''): + return nodeurl(self.location, self.root, id) + + def domainurl(self, id=''): + return domainurl(self.location, self.root, id) + + def consoleurl(self, id=''): + return consoleurl(self.location, self.root, id) + + def deviceurl(self, id=''): + return deviceurl(self.location, self.root, id) + + def vneturl(self, id=''): + return vneturl(self.location, self.root, id) + + def eventurl(self, id=''): + return eventurl(self.location, self.root, id) + + def xend(self): + return xend_get(urljoin(self.location, self.root)) + + def xend_node(self): + return xend_get(self.nodeurl()) + + def xend_node_cpu_rrobin_slice_set(self, slice): + return xend_call(self.nodeurl(), + {'op' : 'cpu_rrobin_slice_set', + 'slice' : slice }) + + def xend_node_cpu_bvt_slice_set(self, slice): + return xend_call(self.nodeurl(), + {'op' : 'cpu_bvt_slice_set', + 'slice' : slice }) + + def xend_domains(self): + return xend_get(self.domainurl()) + + def xend_domain_create(self, conf): + return xend_call(self.domainurl(), + {'op' : 'create', + 'config' : fileof(conf) }) + + def xend_domain(self, id): + return xend_get(self.domainurl(id)) + + def xend_domain_unpause(self, id): + return xend_call(self.domainurl(id), + {'op' : 'unpause'}) + + def xend_domain_pause(self, id): + return xend_call(self.domainurl(id), + {'op' : 'pause'}) + + def xend_domain_shutdown(self, id): + return xend_call(self.domainurl(id), + {'op' : 'shutdown'}) + + def xend_domain_destroy(self, id): + return xend_call(self.domainurl(id), + {'op' : 'destroy'}) + + def xend_domain_save(self, id, filename): + return xend_call(self.domainurl(id), + {'op' : 'save', + 'file' : filename}) + + def xend_domain_restore(self, id, filename): + return xend_call(self.domainurl(id), + {'op' : 'restore', + 'file' : filename }) + + def xend_domain_migrate(self, id, dst): + return xend_call(self.domainurl(id), + {'op' : 'migrate', + 'destination': dst}) + + def xend_domain_pincpu(self, id, cpu): + return xend_call(self.domainurl(id), + {'op' : 'pincpu', + 'cpu' : cpu}) + + def xend_domain_cpu_bvt_set(self, id, mcuadv, warp, warpl, warpu): + return xend_call(self.domainurl(id), + {'op' : 'cpu_bvt_set', + 'mcuadv' : mvuadv, + 'warp' : warp, + 'warpl' : warpl, + 'warpu' : warpu }) + + def xend_domain_cpu_atropos_set(self, id, period, slice, latency, xtratime): + return xend_call(self.domainurl(id), + {'op' : 'cpu_atropos_set', + 'period' : period, + 'slice' : slice, + 'latency' : latency, + 'xtratime': xtratime }) + + def xend_domain_vifs(self, id): + return xend_get(self.domainurl(id), + { 'op' : 'vifs' }) + + def xend_domain_vif_ip_add(self, id, vif, ipaddr): + return xend_call(self.domainurl(id), + {'op' : 'vif_ip_add', + 'vif' : vif, + 'ip' : ipaddr }) + + def xend_domain_vbds(self, id): + return xend_get(self.domainurl(id), + {'op' : 'vbds'}) + + def xend_domain_vbd(self, id, vbd): + return xend_get(self.domainurl(id), + {'op' : 'vbd', + 'vbd' : vbd}) + + def xend_consoles(self): + return xend_get(self.consoleurl()) + + def xend_console(self, id): + return xend_get(self.consoleurl(id)) + + def xend_vnets(self): + return xend_get(self.vneturl()) + + def xend_vnet_create(self, conf): + return xend_call(self.vneturl(), + {'op': 'create', 'config': fileof(conf) }) + + def xend_vnet(self, id): + return xend_get(self.vneturl(id)) + + def xend_vnet_delete(self, id): + return xend_call(self.vneturl(id), + {'op': 'delete'}) + + def xend_event_inject(self, sxpr): + val = xend_call(self.eventurl(), + {'op': 'inject', 'event': fileof(sxpr) }) + + +def main(argv): + """Call an API function: + + python XendClient.py fn args... + + The leading 'xend_' on the function can be omitted. + Example: + + > python XendClient.py domains + (domain 0 8) + > python XendClient.py domain 0 + (domain (id 0) (name Domain-0) (memory 128)) + """ + server = Xend() + fn = argv[1] + if not fn.startswith('xend'): + fn = 'xend_' + fn + args = argv[2:] + val = getattr(server, fn)(*args) + PrettyPrint.prettyprint(val) + print + +if __name__ == "__main__": + main(sys.argv) +else: + server = Xend() diff --git a/tools/xen/lib/xend/XendConsole.py b/tools/xen/lib/xend/XendConsole.py new file mode 100644 index 0000000000..4420c388f2 --- /dev/null +++ b/tools/xen/lib/xend/XendConsole.py @@ -0,0 +1,179 @@ +# Copyright (C) 2004 Mike Wray + +import socket +import xen.ext.xc +xc = xen.ext.xc.new() + +import sxp +import XendRoot +xroot = XendRoot.instance() +import XendDB + +import EventServer +eserver = EventServer.instance() + +from xen.xend.server import SrvDaemon +daemon = SrvDaemon.instance() + +class XendConsoleInfo: + """Console information record. + """ + + def __init__(self, console, dom1, port1, dom2, port2, conn=None): + self.console = console + self.dom1 = int(dom1) + self.port1 = int(port1) + self.dom2 = int(dom2) + self.port2 = int(port2) + self.conn = conn + #self.id = "%d.%d-%d.%d" % (self.dom1, self.port1, self.dom2, self.port2) + self.id = str(port1) + + def __str__(self): + s = "console" + s += " id=%s" % self.id + s += " src=%d.%d" % (self.dom1, self.port1) + s += " dst=%d.%d" % (self.dom2, self.port2) + s += " port=%s" % self.console + if self.conn: + s += " conn=%s:%s" % (self.conn[0], self.conn[1]) + return s + + def sxpr(self): + sxpr = ['console', + ['id', self.id], + ['src', self.dom1, self.port1], + ['dst', self.dom2, self.port2], + ['port', self.console], + ] + if self.conn: + sxpr.append(['connected', self.conn[0], self.conn[1]]) + return sxpr + + def connection(self): + return self.conn + + def update(self, consinfo): + conn = sxp.child(consinfo, 'connected') + if conn: + self.conn = conn[1:] + else: + self.conn = None + + def uri(self): + """Get the uri to use to connect to the console. + This will be a telnet: uri. + + return uri + """ + host = socket.gethostname() + return "telnet://%s:%s" % (host, self.console) + +class XendConsole: + + dbpath = "console" + + def __init__(self): + self.db = XendDB.XendDB(self.dbpath) + self.console = {} + self.console_db = self.db.fetchall("") + if xroot.get_rebooted(): + print 'XendConsole> rebooted: removing all console info' + self.rm_all() + eserver.subscribe('xend.domain.died', self.onDomainDied) + eserver.subscribe('xend.domain.destroy', self.onDomainDied) + + def rm_all(self): + """Remove all console info. Used after reboot. + """ + for (k, v) in self.console_db.items(): + self._delete_console(k) + + def refresh(self): + consoles = daemon.consoles() + cons = {} + for consinfo in consoles: + id = str(sxp.child_value(consinfo, 'id')) + cons[id] = consinfo + if id not in self.console: + self._new_console(consinfo) + for c in self.console.values(): + consinfo = cons.get(c.id) + if consinfo: + c.update(consinfo) + else: + self._delete_console(c.id) + + def onDomainDied(self, event, val): + dom = int(val) + #print 'XendConsole>onDomainDied', 'event', event, "dom=", dom + for c in self.consoles(): + #print 'onDomainDied', "dom=", dom, "dom1=", c.dom1, "dom2=", c.dom2 + if (c.dom1 == dom) or (c.dom2 == dom): + 'XendConsole>onDomainDied', 'delete console dom=', dom + ctrl = daemon.get_domain_console(dom) + if ctrl: + ctrl.close() + self._delete_console(c.id) + + def sync(self): + self.db.saveall("", self.console_db) + + def sync_console(self, id): + self.db.save(id, self.console_db[id]) + + def _new_console(self, consinfo): + # todo: xen needs a call to get current domain id. + dom1 = 0 + port1 = sxp.child_value(consinfo, 'local_port') + dom2 = sxp.child_value(consinfo, 'domain') + port2 = sxp.child_value(consinfo, 'remote_port') + console = sxp.child_value(consinfo, 'console_port') + info = XendConsoleInfo(console, dom1, int(port1), int(dom2), int(port2)) + info.update(consinfo) + self._add_console(info.id, info) + return info + + def _add_console(self, id, info): + self.console[id] = info + self.console_db[id] = info.sxpr() + self.sync_console(id) + + def _delete_console(self, id): + if id in self.console: + del self.console[id] + if id in self.console_db: + del self.console_db[id] + self.db.delete(id) + + def console_ls(self): + self.refresh() + return self.console.keys() + + def consoles(self): + self.refresh() + return self.console.values() + + def console_create(self, dom): + consinfo = daemon.console_create(dom) + info = self._new_console(consinfo) + return info + + def console_get(self, id): + self.refresh() + return self.console.get(id) + + def console_delete(self, id): + self._delete_console(id) + + def console_disconnect(self, id): + id = int(id) + daemon.console_disconnect(id) + +def instance(): + global inst + try: + inst + except: + inst = XendConsole() + return inst diff --git a/tools/xen/lib/xend/XendDB.py b/tools/xen/lib/xend/XendDB.py new file mode 100644 index 0000000000..6a27e65b58 --- /dev/null +++ b/tools/xen/lib/xend/XendDB.py @@ -0,0 +1,91 @@ +# Copyright (C) 2004 Mike Wray + +import os +import os.path +import errno +import dircache +import time + +import sxp +import XendRoot +xroot = XendRoot.instance() + +class XendDB: + """Persistence for Xend. Stores data in files and directories. + """ + + def __init__(self, path=None): + self.dbpath = xroot.get_dbroot() + if path: + self.dbpath = os.path.join(self.dbpath, path) + pass + + def filepath(self, path): + return os.path.join(self.dbpath, path) + + def fetch(self, path): + fpath = self.filepath(path) + return self.fetchfile(fpath) + + def fetchfile(self, fpath): + pin = sxp.Parser() + fin = file(fpath, "rb") + try: + while 1: + try: + buf = fin.read(1024) + except IOError, ex: + if ex.errno == errno.EINTR: + continue + else: + raise + pin.input(buf) + if buf == '': + pin.input_eof() + break + finally: + fin.close() + return pin.get_val() + + def save(self, path, sxpr): + fpath = self.filepath(path) + return self.savefile(fpath, sxpr) + + def savefile(self, fpath, sxpr): + fdir = os.path.dirname(fpath) + if not os.path.isdir(fdir): + os.makedirs(fdir) + fout = file(fpath, "wb+") + try: + t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + fout.write("# %s %s\n" % (fpath, t)) + sxp.show(sxpr, out=fout) + finally: + fout.close() + + def fetchall(self, path): + dpath = self.filepath(path) + d = {} + for k in dircache.listdir(dpath): + try: + v = self.fetchfile(os.path.join(dpath, k)) + d[k] = v + except: + pass + return d + + def saveall(self, path, d): + for (k, v) in d.items(): + self.save(os.path.join(path, k), v) + + def delete(self, path): + dpath = self.filepath(path) + os.unlink(dpath) + + def ls(self, path): + dpath = self.filepath(path) + return dircache.listdir(dpath) + + + + diff --git a/tools/xen/lib/xend/XendDomain.py b/tools/xen/lib/xend/XendDomain.py new file mode 100644 index 0000000000..446b7f7fae --- /dev/null +++ b/tools/xen/lib/xend/XendDomain.py @@ -0,0 +1,366 @@ +# Copyright (C) 2004 Mike Wray + +"""Handler for domain operations. + Nothing here is persistent (across reboots). + Needs to be persistent for one uptime. +""" +import sys + +from twisted.internet import defer + +import xen.ext.xc; xc = xen.ext.xc.new() +import xenctl.ip + +import sxp +import XendRoot +xroot = XendRoot.instance() +import XendDB +import XendDomainInfo +import XendConsole +import EventServer + +from xen.xend.server import SrvDaemon +xend = SrvDaemon.instance() + +eserver = EventServer.instance() + +__all__ = [ "XendDomain" ] + +class XendDomain: + """Index of all domains. Singleton. + """ + + dbpath = "domain" + domain = {} + + def __init__(self): + self.xconsole = XendConsole.instance() + # Table of domain info indexed by domain id. + self.db = XendDB.XendDB(self.dbpath) + #self.domain = {} + self.domain_db = self.db.fetchall("") + if xroot.get_rebooted(): + print 'XendDomain> rebooted: removing all domain info' + self.rm_all() + eserver.subscribe('xend.virq', self.onVirq) + self.initial_refresh() + + def onVirq(self, event, val): + print 'XendDomain> virq', val + self.reap() + + def rm_all(self): + """Remove all domain info. Used after reboot. + """ + for (k, v) in self.domain_db.items(): + self._delete_domain(k, notify=0) + + def initial_refresh(self): + """Refresh initial domain info from domain_db. + """ + print "initial_refresh>" + for d in self.domain_db.values(): print 'db dom=', d + domlist = xc.domain_getinfo() + for d in domlist: print 'xc dom=', d + doms = {} + for d in domlist: + domid = str(d['dom']) + doms[domid] = d + dlist = [] + for config in self.domain_db.values(): + domid = str(sxp.child_value(config, 'id')) + print "dom=", domid, "config=", config + if domid in doms: + print "dom=", domid, "new" + deferred = self._new_domain(config, doms[domid]) + dlist.append(deferred) + else: + print "dom=", domid, "del" + self._delete_domain(domid) + deferred = defer.DeferredList(dlist, fireOnOneErrback=1) + def cbok(val): + #print "doms:" + #for d in self.domain.values(): print 'dom', d + self.refresh() + print "XendDomain>initial_refresh> doms:" + for d in self.domain.values(): print 'dom', d + deferred.addCallback(cbok) + + def sync(self): + """Sync domain db to disk. + """ + self.db.saveall("", self.domain_db) + + def sync_domain(self, dom): + """Sync info for a domain to disk. + + dom domain id (string) + """ + self.db.save(dom, self.domain_db[dom]) + + def close(self): + pass + + def _new_domain(self, savedinfo, info): + """Create a domain entry from saved info. + """ +## console = None +## kernel = None +## id = sxp.child_value(info, 'id') +## dom = int(id) +## name = sxp.child_value(info, 'name') +## memory = int(sxp.child_value(info, 'memory')) +## consoleinfo = sxp.child(info, 'console') +## if consoleinfo: +## consoleid = sxp.child_value(consoleinfo, 'id') +## console = self.xconsole.console_get(consoleid) +## if dom and console is None: +## # Try to connect a console. +## console = self.xconsole.console_create(dom) +## config = sxp.child(info, 'config') +## if config: +## image = sxp.child(info, 'image') +## if image: +## image = sxp.child0(image) +## kernel = sxp.child_value(image, 'kernel') +## dominfo = XendDomainInfo.XendDomainInfo( +## config, dom, name, memory, kernel, console) + config = sxp.child_value(savedinfo, 'config') + deferred = XendDomainInfo.vm_recreate(config, info) + def fn(dominfo): + self.domain[dominfo.id] = dominfo + deferred.addCallback(fn) + return deferred + + def _add_domain(self, id, info, notify=1): + self.domain[id] = info + self.domain_db[id] = info.sxpr() + self.sync_domain(id) + if notify: eserver.inject('xend.domain.created', id) + + def _delete_domain(self, id, notify=1): + if id in self.domain: + if notify: eserver.inject('xend.domain.died', id) + del self.domain[id] + if id in self.domain_db: + del self.domain_db[id] + self.db.delete(id) + + def reap(self): + """Go through the domains looking for ones that have crashed or stopped. + Tidy them up. + """ + print 'XendDomain>reap>' + domlist = xc.domain_getinfo() + casualties = [] + for d in domlist: + #print 'dom', d + dead = 0 + dead = dead or (d['crashed'] or d['shutdown']) + dead = dead or (d['dying'] and + not(d['running'] or d['paused'] or d['blocked'])) + if dead: + casualties.append(d) + for d in casualties: + id = str(d['dom']) + print 'XendDomain>reap> died id=', id, d + dominfo = self.domain.get(id) + if not dominfo: continue + dominfo.died() + self.domain_destroy(id, refresh=0) + print 'XendDomain>reap<' + + def refresh(self): + """Refresh domain list from Xen. + """ + domlist = xc.domain_getinfo() + # Index the domlist by id. + # Add entries for any domains we don't know about. + doms = {} + for d in domlist: + id = str(d['dom']) + doms[id] = d + if id not in self.domain: + config = None + #image = None + #newinfo = XendDomainInfo.XendDomainInfo( + # config, d['dom'], d['name'], d['mem_kb']/1024, image=image, info=d) + deferred = XendDomainInfo.vm_recreate(config, d) + def fn(dominfo): + self._add_domain(dominfo.id, dominfo) + deferred.addCallback(fn) + # Remove entries for domains that no longer exist. + for d in self.domain.values(): + dominfo = doms.get(d.id) + if dominfo: + d.update(dominfo) + else: + self._delete_domain(d.id) + self.reap() + + def refresh_domain(self, id): + dom = int(id) + dominfo = xc.domain_getinfo(dom, 1) + if dominfo == [] or dominfo[0]['dom'] != dom: + try: + self._delete_domain(id) + except: + print 'refresh_domain: error' + raise + pass + else: + d = self.domain.get(id) + if d: + d.update(dominfo[0]) + + def domain_ls(self): + # List domains. + # Update info from kernel first. + self.refresh() + return self.domain.keys() + + def domains(self): + self.refresh() + return self.domain.values() + + def domain_create(self, config): + # Create domain, log it. + deferred = XendDomainInfo.vm_create(config) + def fn(dominfo): + self._add_domain(dominfo.id, dominfo) + return dominfo + deferred.addCallback(fn) + return deferred + + def domain_get(self, id): + id = str(id) + self.refresh_domain(id) + return self.domain[id] + + def domain_unpause(self, id): + """(Re)start domain running. + """ + dom = int(id) + eserver.inject('xend.domain.unpause', id) + return xc.domain_unpause(dom=dom) + + def domain_pause(self, id): + """Pause domain execution. + """ + dom = int(id) + eserver.inject('xend.domain.pause', id) + return xc.domain_pause(dom=dom) + + def domain_shutdown(self, id, reason='poweroff'): + """Shutdown domain (nicely). + """ + dom = int(id) + if dom <= 0: + return 0 + eserver.inject('xend.domain.shutdown', [id, reason]) + val = xend.domain_shutdown(dom, reason) + self.refresh() + return val + + def domain_destroy(self, id, refresh=1): + """Terminate domain immediately. + """ + dom = int(id) + if dom <= 0: + return 0 + eserver.inject('xend.domain.destroy', id) + val = xc.domain_destroy(dom=dom) + if refresh: self.refresh() + return val + + def domain_migrate(self, id, dst): + """Start domain migration. + """ + # Need a cancel too? + pass + + def domain_save(self, id, dst, progress=0): + """Save domain state to file, destroy domain. + """ + dom = int(id) + self.domain_pause(id) + eserver.inject('xend.domain.save', id) + rc = xc.linux_save(dom=dom, state_file=dst, progress=progress) + if rc == 0: + self.domain_destroy(id) + return rc + + def domain_restore(self, src, config, progress=0): + """Restore domain from file. + """ + dominfo = XendDomainInfo.dom_restore(dom, config) + self._add_domain(dominfo.id, dominfo) + return dominfo + + #============================================================================ + # Backward compatibility stuff from here on. + + def domain_pincpu(self, dom, cpu): + dom = int(dom) + return xc.domain_pincpu(dom, cpu) + + def domain_cpu_bvt_set(self, dom, mcuadv, warp, warpl, warpu): + dom = int(dom) + return xc.bvtsched_domain_set(dom=dom, mcuadv=mcuadv, + warp=warp, warpl=warpl, warpu=warpu) + + def domain_cpu_bvt_get(self, dom): + dom = int(dom) + return xc.bvtsched_domain_get(dom) + + def domain_cpu_atropos_set(self, dom, period, slice, latency, xtratime): + dom = int(dom) + return xc.atropos_domain_set(dom, period, slice, latency, xtratime) + + def domain_cpu_atropos_get(self, dom): + dom = int(dom) + return xc.atropos_domain_get(dom) + + def domain_vif_ls(self, dom): + dominfo = self.domain_get(dom) + if not dominfo: return None + devs = dominfo.get_devices('vif') + return range(0, len(devs)) + + def domain_vif_get(self, dom, vif): + dominfo = self.domain_get(dom) + if not dominfo: return None + return dominfo.get_device_by_index(vif) + + def domain_vif_ip_add(self, dom, vif, ip): + dom = int(dom) + return xenctl.ip.setup_vfr_rules_for_vif(dom, vif, ip) + + def domain_vbd_ls(self, dom): + dominfo = self.domain_get(dom) + if not dominfo: return [] + devs = dominfo.get_devices('vbd') + return [ sxp.child_value(v, 'dev') for v in devs ] + + def domain_vbd_get(self, dom, vbd): + dominfo = self.domain_get(dom) + if not dominfo: return None + devs = dominfo.get_devices('vbd') + for v in devs: + if sxp.child_value(v, 'dev') == vbd: + return v + return None + + def domain_shadow_control(self, dom, op): + dom = int(dom) + return xc.shadow_control(dom, op) + + #============================================================================ + +def instance(): + global inst + try: + inst + except: + inst = XendDomain() + return inst diff --git a/tools/xen/lib/xend/XendDomainConfig.py b/tools/xen/lib/xend/XendDomainConfig.py new file mode 100644 index 0000000000..35db31ff51 --- /dev/null +++ b/tools/xen/lib/xend/XendDomainConfig.py @@ -0,0 +1,44 @@ +# Copyright (C) 2004 Mike Wray + +"""Handler for persistent domain configs. + +""" + +import sxp +import XendDB +import XendDomain + +__all__ = [ "XendDomainConfig" ] + +class XendDomainConfig: + + dbpath = 'config' + + def __init__(self): + self.db = XendDB.XendDB(self.dbpath) + + def domain_config_ls(self, path): + return self.db.ls(path) + + def domain_config_create(self, path, sxpr): + self.db.save(path, sxpr) + pass + + def domain_config_delete(self, path): + self.db.delete(path) + + def domain_config_instance(self, path): + """Create a domain from a config. + """ + config = self.db.fetch(path) + xd = XendDomain.instance() + newdom = xd.domain_create(config) + return newdom + +def instance(): + global inst + try: + inst + except: + inst = XendDomainConfig() + return inst diff --git a/tools/xen/lib/xend/XendDomainInfo.py b/tools/xen/lib/xend/XendDomainInfo.py new file mode 100644 index 0000000000..964285ec13 --- /dev/null +++ b/tools/xen/lib/xend/XendDomainInfo.py @@ -0,0 +1,911 @@ +#!/usr/bin/python +# Copyright (C) 2004 Mike Wray + +"""Representation of a single domain. +Includes support for domain construction, using +open-ended configurations. + +Author: Mike Wray + +""" + +import string +import re +import sys +import os + +from twisted.internet import defer + +import xen.ext.xc; xc = xen.ext.xc.new() +import xenctl.ip + +import sxp + +import XendConsole +xendConsole = XendConsole.instance() + +import XendBridge + +import server.SrvDaemon +xend = server.SrvDaemon.instance() + +SIF_BLK_BE_DOMAIN = (1<<4) +SIF_NET_BE_DOMAIN = (1<<5) + +def readlines(fd): + """Version of readlines safe against EINTR. + """ + import errno + + lines = [] + while 1: + try: + line = fd.readline() + except IOError, ex: + if ex.errno == errno.EINTR: + continue + else: + raise + if line == '': break + lines.append(line) + return lines + +class VmError(ValueError): + """Vm construction error.""" + + def __init__(self, value): + self.value = value + + def __str__(self): + return self.value + + +def blkdev_name_to_number(name): + """Take the given textual block-device name (e.g., '/dev/sda1', + 'hda') and return the device number used by the OS. """ + + if not re.match( '/dev/', name ): + name = '/dev/' + name + + return os.stat(name).st_rdev + +def lookup_raw_partn(partition): + """Take the given block-device name (e.g., '/dev/sda1', 'hda') + and return a dictionary { device, start_sector, + nr_sectors, type } + device: Device number of the given partition + start_sector: Index of first sector of the partition + nr_sectors: Number of sectors comprising this partition + type: 'Disk' or identifying name for partition type + """ + + if not re.match( '/dev/', partition ): + partition = '/dev/' + partition + + drive = re.split( '[0-9]', partition )[0] + + if drive == partition: + fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' ) + line = readline(fd) + if line: + return [ { 'device' : blkdev_name_to_number(drive), + 'start_sector' : long(0), + 'nr_sectors' : long(line) * 2, + 'type' : 'Disk' } ] + return None + + # determine position on disk + fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' ) + + #['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012'] + lines = readlines(fd) + for line in lines: + m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' + + 'size=\s*([0-9]+), Id=\s*(\S+).*$', line) + if m: + return [ { 'device' : blkdev_name_to_number(drive), + 'start_sector' : long(m.group(1)), + 'nr_sectors' : long(m.group(2)), + 'type' : m.group(3) } ] + + return None + +def lookup_disk_uname(uname): + """Lookup a list of segments for a physical device. + uname [string]: name of the device in the format \'phy:dev\' for a physical device + returns [list of dicts]: list of extents that make up the named device + """ + ( type, d_name ) = string.split( uname, ':' ) + + if type == "phy": + segments = lookup_raw_partn( d_name ) + else: + segments = None + return segments + +def make_disk(dom, uname, dev, mode, recreate=0): + """Create a virtual disk device for a domain. + + @returns Deferred + """ + segments = lookup_disk_uname(uname) + if not segments: + raise VmError("vbd: Segments not found: uname=%s" % uname) + if len(segments) > 1: + raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname) + segment = segments[0] + vdev = blkdev_name_to_number(dev) + ctrl = xend.blkif_create(dom, recreate=recreate) + + def fn(ctrl): + return xend.blkif_dev_create(dom, vdev, mode, segment, recreate=recreate) + ctrl.addCallback(fn) + return ctrl + +def make_vif(dom, vif, vmac, recreate=0): + """Create a virtual network device for a domain. + + + @returns Deferred + """ + xend.netif_create(dom, recreate=recreate) + d = xend.netif_dev_create(dom, vif, vmac, recreate=recreate) + return d + +def vif_up(iplist): + """send an unsolicited ARP reply for all non link-local IP addresses. + + iplist IP addresses + """ + + IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind' + + def get_ip_nonlocal_bind(): + return int(open(IP_NONLOCAL_BIND, 'r').read()[0]) + + def set_ip_nonlocal_bind(v): + print >> open(IP_NONLOCAL_BIND, 'w'), str(v) + + def link_local(ip): + return xenctl.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0') + + def arping(ip, gw): + cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw) + print cmd + os.system(cmd) + + gateway = xenctl.ip.get_current_ipgw() or '255.255.255.255' + nlb = get_ip_nonlocal_bind() + if not nlb: set_ip_nonlocal_bind(1) + try: + for ip in iplist: + if not link_local(ip): + arping(ip, gateway) + finally: + if not nlb: set_ip_nonlocal_bind(0) + +config_handlers = {} + +def add_config_handler(name, h): + """Add a handler for a config field. + + name field name + h handler: fn(vm, config, field, index) + """ + config_handlers[name] = h + +def get_config_handler(name): + """Get a handler for a config field. + + returns handler or None + """ + return config_handlers.get(name) + +"""Table of handlers for virtual machine images. +Indexed by image type. +""" +image_handlers = {} + +def add_image_handler(name, h): + """Add a handler for an image type + name image type + h handler: fn(config, name, memory, image) + """ + image_handlers[name] = h + +def get_image_handler(name): + """Get the handler for an image type. + name image type + + returns handler or None + """ + return image_handlers.get(name) + +"""Table of handlers for devices. +Indexed by device type. +""" +device_handlers = {} + +def add_device_handler(name, h): + """Add a handler for a device type. + + name device type + h handler: fn(vm, dev) + """ + device_handlers[name] = h + +def get_device_handler(name): + """Get the handler for a device type. + + name device type + + returns handler or None + """ + return device_handlers.get(name) + +def vm_create(config): + """Create a VM from a configuration. + If a vm has been partially created and there is an error it + is destroyed. + + config configuration + + returns Deferred + raises VmError for invalid configuration + """ + print 'vm_create>' + vm = XendDomainInfo() + return vm.construct(config) + +def vm_recreate(config, info): + """Create the VM object for an existing domain. + """ + vm = XendDomainInfo() + vm.recreate = 1 + vm.setdom(info['dom']) + vm.name = info['name'] + vm.memory = info['mem_kb']/1024 + if config: + d = vm.construct(config) + else: + d = defer.Deferred() + d.callback(vm) + return d + +def vm_restore(src, config, progress=0): + """Restore a VM from a disk image. + + src saved state to restore + config configuration + progress progress reporting flag + returns deferred + raises VmError for invalid configuration + """ + vm = XendDomainInfo() + vm.config = config + ostype = "linux" #todo set from config + restorefn = getattr(xc, "%s_restore" % ostype) + dom = restorefn(state_file=src, progress=progress) + if dom < 0: + raise VMError('restore failed') + deferred = vm.dom_configure(dom) + def vifs_cb(val, vm): + vif_up(vm.ipaddrs) + deferred.addCallback(vifs_cb, vm) + return deferred + +def dom_get(dom): + domlist = xc.domain_getinfo(dom=dom) + if domlist and dom == domlist[0]['dom']: + return domlist[0] + return None + + +def append_deferred(dlist, v): + if isinstance(v, defer.Deferred): + dlist.append(v) + +def _vm_configure1(val, vm): + d = vm.create_devices() + print '_vm_configure1> made devices...' + def cbok(x): + print '_vm_configure1> cbok', x + return x + d.addCallback(cbok) + d.addCallback(_vm_configure2, vm) + print '_vm_configure1<' + return d + +def _vm_configure2(val, vm): + print '>callback _vm_configure2...' + d = vm.configure_fields() + def cbok(results): + print '_vm_configure2> cbok', results + return vm + def cberr(err): + print '_vm_configure2> cberr', err + vm.destroy() + return err + d.addCallback(cbok) + d.addErrback(cberr) + print '<_vm_configure2' + return d + +class XendDomainInfo: + """Virtual machine object.""" + + def __init__(self): + self.recreate = 0 + self.config = None + self.id = None + self.dom = None + self.name = None + self.memory = None + self.image = None + self.ramdisk = None + self.cmdline = None + self.console = None + self.devices = {} + self.configs = [] + self.info = None + self.ipaddrs = [] + self.blkif_backend = 0 + self.netif_backend = 0 + #todo: state: running, suspended + self.state = 'running' + #todo: set to migrate info if migrating + self.migrate = None + + def setdom(self, dom): + self.dom = int(dom) + self.id = str(dom) + + def update(self, info): + """Update with info from xc.domain_getinfo(). + """ + self.info = info + self.memory = self.info['mem_kb'] / 1024 + + def __str__(self): + s = "domain" + s += " id=" + self.id + s += " name=" + self.name + s += " memory=" + str(self.memory) + if self.console: + s += " console=" + self.console.id + if self.image: + s += " image=" + self.image + s += "" + return s + + __repr__ = __str__ + + def sxpr(self): + sxpr = ['domain', + ['id', self.id], + ['name', self.name], + ['memory', self.memory] ] + if self.info: + run = (self.info['running'] and 'r') or '-' + block = (self.info['blocked'] and 'b') or '-' + stop = (self.info['paused'] and 'p') or '-' + susp = (self.info['shutdown'] and 's') or '-' + crash = (self.info['crashed'] and 'c') or '-' + state = run + block + stop + susp + crash + sxpr.append(['state', state]) + if self.info['shutdown']: + reasons = ["poweroff", "reboot", "suspend"] + reason = reasons[self.info['shutdown_reason']] + sxpr.append(['shutdown_reason', reason]) + sxpr.append(['cpu', self.info['cpu']]) + sxpr.append(['cpu_time', self.info['cpu_time']/1e9]) + if self.console: + sxpr.append(self.console.sxpr()) + if self.config: + sxpr.append(['config', self.config]) + return sxpr + + def construct(self, config): + # todo - add support for scheduling params? + self.config = config + try: + self.name = sxp.child_value(config, 'name') + self.memory = int(sxp.child_value(config, 'memory', '128')) + self.configure_backends() + image = sxp.child_value(config, 'image') + image_name = sxp.name(image) + image_handler = get_image_handler(image_name) + if image_handler is None: + raise VmError('unknown image type: ' + image_name) + image_handler(self, image) + deferred = self.configure() + except StandardError, ex: + # Catch errors, cleanup and re-raise. + self.destroy() + raise + def cbok(x): + print 'vm_create> cbok', x + return x + deferred.addCallback(cbok) + print 'vm_create<' + return deferred + + def config_devices(self, name): + """Get a list of the 'device' nodes of a given type from the config. + + name device type + return list of device configs + """ + devices = [] + for d in sxp.children(self.config, 'device'): + dev = sxp.child0(d) + if dev is None: continue + if name == sxp.name(dev): + devices.append(dev) + return devices + + def add_device(self, type, dev): + """Add a device to a virtual machine. + + dev device to add + """ + dl = self.devices.get(type, []) + dl.append(dev) + self.devices[type] = dl + + def get_devices(self, type): + val = self.devices.get(type, []) + return val + + def get_device_by_id(self, type, id): + """Get the device with the given id. + + id device id + + returns device or None + """ + dl = self.get_devices(type) + for d in dl: + if d.getprop('id') == id: + return d + return None + + def get_device_by_index(self, type, idx): + """Get the device with the given index. + + idx device index + + returns device or None + """ + dl = self.get_devices(type) + if 0 <= idx < len(dl): + return dl[idx] + else: + return None + + def add_config(self, val): + """Add configuration data to a virtual machine. + + val data to add + """ + self.configs.append(val) + + def destroy(self): + if self.dom <= 0: + return 0 + return xc.domain_destroy(dom=self.dom) + + def died(self): + print 'died>', self.dom + self.release_devices() + + def release_devices(self): + print 'release_devices>', self.dom + self.release_vifs() + self.release_vbds() + self.devices = {} + + def release_vifs(self): + print 'release_vifs>', self.dom + if self.dom is None: return + ctrl = xend.netif_get(self.dom) + if ctrl: + ctrl.destroy() + + def release_vbds(self): + print 'release_vbds>', self.dom + if self.dom is None: return + ctrl = xend.blkif_get(self.dom) + if ctrl: + ctrl.destroy() + + def show(self): + """Print virtual machine info. + """ + print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory) + print "image:" + sxp.show(self.image) + print + for dl in self.devices: + for dev in dl: + print "device:" + sxp.show(dev) + print + for val in self.configs: + print "config:" + sxp.show(val) + print + print "]" + + def init_domain(self): + """Initialize the domain memory. + """ + if self.recreate: return + memory = self.memory + name = self.name + cpu = int(sxp.child_value(self.config, 'cpu', '-1')) + print 'init_domain>', memory, name, cpu + dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu) + if dom <= 0: + raise VmError('Creating domain failed: name=%s memory=%d' + % (name, memory)) + self.setdom(dom) + + def build_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n): + """Build the domain boot image. + """ + if self.recreate: return + if len(cmdline) >= 256: + print 'Warning: kernel cmdline too long' + dom = self.dom + buildfn = getattr(xc, '%s_build' % ostype) + print 'build_domain>', ostype, dom, kernel, cmdline, ramdisk + flags = 0 + if self.netif_backend: flags |= SIF_NET_BE_DOMAIN + if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN + err = buildfn(dom = dom, + image = kernel, + control_evtchn = self.console.port2, + cmdline = cmdline, + ramdisk = ramdisk, + flags = flags) + if err != 0: + raise VmError('Building domain failed: type=%s dom=%d err=%d' + % (ostype, dom, err)) + + def create_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n): + """Create a domain. Builds the image but does not configure it. + + ostype OS type + kernel kernel image + ramdisk kernel ramdisk + cmdline kernel commandline + vifs_n number of network interfaces + """ + print 'create_domain>', ostype, kernel + if not self.recreate: + if not os.path.isfile(kernel): + raise VmError('Kernel image does not exist: %s' % kernel) + if ramdisk and not os.path.isfile(ramdisk): + raise VMError('Kernel ramdisk does not exist: %s' % ramdisk) + print 'create-domain> init_domain...' + self.init_domain() + print 'create_domain>', 'dom=', self.dom + self.console = xendConsole.console_create(self.dom) + self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n) + self.image = kernel + self.ramdisk = ramdisk + self.cmdline = cmdline + + def create_devices(self): + """Create the devices for a vm. + + returns Deferred + raises VmError for invalid devices + """ + print '>create_devices' + dlist = [] + devices = sxp.children(self.config, 'device') + index = {} + for d in devices: + dev = sxp.child0(d) + if dev is None: + raise VmError('invalid device') + dev_name = sxp.name(dev) + dev_index = index.get(dev_name, 0) + dev_handler = get_device_handler(dev_name) + if dev_handler is None: + raise VmError('unknown device type: ' + dev_name) + v = dev_handler(self, dev, dev_index) + append_deferred(dlist, v) + index[dev_name] = dev_index + 1 + deferred = defer.DeferredList(dlist, fireOnOneErrback=1) + print ' created', dev + return id + defer.addCallback(fn) + return defer + +def vm_dev_vbd(vm, val, index): + """Create a virtual block device (vbd). + + vm virtual machine + val vbd config + index vbd index + """ + if vm.blkif_backend: + raise VmError('vbd: vbd in blkif backend domain') + vdev = index + uname = sxp.child_value(val, 'uname') + if not uname: + raise VMError('vbd: Missing uname') + dev = sxp.child_value(val, 'dev') + if not dev: + raise VMError('vbd: Missing dev') + mode = sxp.child_value(val, 'mode', 'r') + defer = make_disk(vm.dom, uname, dev, mode, vm.recreate) + def fn(vbd): + dev = xend.blkif_dev(vm.dom, vdev) + vm.add_device('vbd', dev) + return vbd + defer.addCallback(fn) + return defer + +def parse_pci(val): + if isinstance(val, StringType): + radix = 10 + if val.startswith('0x') or val.startswith('0X'): + radix = 16 + v = int(val, radix) + else: + v = val + return v + +def vm_dev_pci(vm, val, index): + bus = sxp.child_value(val, 'bus') + if not bus: + raise VMError('pci: Missing bus') + dev = sxp.child_value(val, 'dev') + if not dev: + raise VMError('pci: Missing dev') + func = sxp.child_value(val, 'func') + if not func: + raise VMError('pci: Missing func') + try: + bus = parse_pci(bus) + dev = parse_pci(dev) + func = parse_pci(func) + except: + raise VMError('pci: invalid parameter') + rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev, + func=func, enable=1) + if rc < 0: + #todo non-fatal + raise VMError('pci: Failed to configure device: bus=%s dev=%s func=%s' % + (bus, dev, func)) + return rc + + +def vm_field_vfr(vm, config, val, index): + """Handle a vfr field in a config. + + vm virtual machine + config vm config + val vfr field + """ + # Get the rules and add them. + # (vfr (vif (id foo) (ip x.x.x.x)) ... ) + list = sxp.children(val, 'vif') + ipaddrs = [] + for v in list: + id = sxp.child_value(v, 'id') + if id is None: + raise VmError('vfr: missing vif id') + id = int(id) + dev = vm.get_device_by_index('vif', id) + if not dev: + raise VmError('vfr: invalid vif id %d' % id) + vif = sxp.child_value(dev, 'vif') + ip = sxp.child_value(v, 'ip') + if not ip: + raise VmError('vfr: missing ip address') + ipaddrs.append(ip); + #Don't do this in new i/o model. + #print 'vm_field_vfr> add rule', 'dom=', vm.dom, 'vif=', vif, 'ip=', ip + #xenctl.ip.setup_vfr_rules_for_vif(vm.dom, vif, ip) + vm.ipaddrs = ipaddrs + +def vnet_bridge(vnet, vmac, dom, idx): + """Add the device for the vif to the bridge for its vnet. + """ + vif = "vif%d.%d" % (dom, idx) + try: + cmd = "(vif.conn (vif %s) (vnet %s) (vmac %s))" % (vif, vnet, vmac) + print "*** vnet_bridge>", cmd + out = file("/proc/vnet/policy", "wb") + out.write(cmd) + err = out.close() + print "vnet_bridge>", "err=", err + except IOError, ex: + print "vnet_bridge>", ex + +def vm_field_vnet(vm, config, val, index): + """Handle a vnet field in a config. + + vm virtual machine + config vm config + val vnet field + index index + """ + # Get the vif children. For each vif look up the vif device + # with the given id and configure its vnet. + # (vnet (vif (id foo) (vnet 2) (mac x:x:x:x:x:x)) ... ) + vif_vnets = sxp.children(val, 'vif') + for v in vif_vnets: + id = sxp.child_value(v, 'id') + if id is None: + raise VmError('vnet: missing vif id') + dev = vm.get_device_by_id('vif', id) + #vnet = sxp.child_value(v, 'vnet', 1) + #mac = sxp.child_value(dev, 'mac') + #vif = sxp.child_value(dev, 'vif') + #vnet_bridge(vnet, mac, vm.dom, 0) + #vm.add_config([ 'vif.vnet', ['id', id], ['vnet', vnet], ['mac', mac]]) + +# Register image handlers for linux and bsd. +add_image_handler('linux', vm_image_linux) +add_image_handler('netbsd', vm_image_netbsd) + +# Register device handlers for vifs and vbds. +add_device_handler('vif', vm_dev_vif) +add_device_handler('vbd', vm_dev_vbd) +add_device_handler('pci', vm_dev_pci) + +# Register config handlers for vfr and vnet. +add_config_handler('vfr', vm_field_vfr) +add_config_handler('vnet', vm_field_vnet) diff --git a/tools/xen/lib/xend/XendMigrate.py b/tools/xen/lib/xend/XendMigrate.py new file mode 100644 index 0000000000..1580ba83ed --- /dev/null +++ b/tools/xen/lib/xend/XendMigrate.py @@ -0,0 +1,103 @@ +# Copyright (C) 2004 Mike Wray + +import sys +import socket + +import sxp +import XendDB +import EventServer; eserver = EventServer.instance() + +class XendMigrateInfo: + + # states: begin, active, failed, succeeded? + + def __init__(self, id, dom, dst): + self.id = id + self.state = 'begin' + self.src_host = socket.gethostname() + self.src_dom = dom + self.dst_host = dst + self.dst_dom = None + + def set_state(self, state): + self.state = state + + def get_state(self): + return self.state + + def sxpr(self): + sxpr = ['migrate', ['id', self.id], ['state', self.state] ] + sxpr_src = ['src', ['host', self.src_host], ['domain', self.src_dom] ] + sxpr.append(sxpr_src) + sxpr_dst = ['dst', ['host', self.dst] ] + if self.dst_dom: + sxpr_dst.append(['domain', self.dst_dom]) + sxpr.append(sxpr_dst) + return sxpr + + +class XendMigrate: + # Represents migration in progress. + # Use log for indications of begin/end/errors? + # Need logging of: domain create/halt, migrate begin/end/fail + # Log via event server? + + dbpath = "migrate" + + def __init__(self): + self.db = XendDB.XendDB(self.dbpath) + self.migrate = {} + self.migrate_db = self.db.fetchall("") + self.id = 0 + + def nextid(self): + self.id += 1 + return "%d" % self.id + + def sync(self): + self.db.saveall("", self.migrate_db) + + def sync_migrate(self, id): + self.db.save(id, self.migrate_db[id]) + + def close(self): + pass + + def _add_migrate(self, id, info): + self.migrate[id] = info + self.migrate_db[id] = info.sxpr() + self.sync_migrate(id) + #eserver.inject('xend.migrate.begin', info.sxpr()) + + def _delete_migrate(self, id): + #eserver.inject('xend.migrate.end', id) + del self.migrate[id] + del self.migrate_db[id] + self.db.delete(id) + + def migrate_ls(self): + return self.migrate.keys() + + def migrates(self): + return self.migrate.values() + + def migrate_get(self, id): + return self.migrate.get(id) + + def migrate_begin(self, dom, dst): + # Check dom for existence, not migrating already. + # Create migrate info, tell xend to migrate it? + # - or fork migrate command ourselves? + # Subscribe to migrate notifications (for updating). + id = self.nextid() + info = XenMigrateInfo(id, dom, dst) + self._add_migrate(id, info) + return id + +def instance(): + global inst + try: + inst + except: + inst = XendMigrate() + return inst diff --git a/tools/xen/lib/xend/XendNode.py b/tools/xen/lib/xend/XendNode.py new file mode 100644 index 0000000000..7221785aff --- /dev/null +++ b/tools/xen/lib/xend/XendNode.py @@ -0,0 +1,71 @@ +# Copyright (C) 2004 Mike Wray + +"""Handler for node operations. + Has some persistent state: + - logs + - notification urls + +""" + +import os +import xen.ext.xc + +class XendNode: + + def __init__(self): + self.xc = xen.ext.xc.new() + + def shutdown(self): + return 0 + + def reboot(self): + return 0 + + def notify(self, uri): + return 0 + + def cpu_bvt_slice_set(self, slice): + ret = 0 + #ret = self.xc.bvtsched_global_set(ctx_allow=slice) + return ret + + def cpu_bvt_slice_get(self, slice): + ret = 0 + #ret = self.xc.bvtsched_global_get() + return ret + + def cpu_rrobin_slice_set(self, slice): + ret = 0 + #ret = self.xc.rrobin_global_set(slice) + return ret + + def info(self): + return self.nodeinfo() + self.physinfo() + + def nodeinfo(self): + (sys, host, rel, ver, mch) = os.uname() + return [['system', sys], + ['host', host], + ['release', rel], + ['version', ver], + ['machine', mch]] + + def physinfo(self): + pinfo = self.xc.physinfo() + info = [['cores', pinfo['cores']], + ['hyperthreads_per_core', pinfo['ht_per_core']], + ['cpu_mhz', pinfo['cpu_khz']/1000], + ['memory', pinfo['total_pages']/256], + ['free_memory', pinfo['free_pages']/256]] + return info + + + +def instance(): + global inst + try: + inst + except: + inst = XendNode() + return inst + diff --git a/tools/xen/lib/xend/XendRoot.py b/tools/xen/lib/xend/XendRoot.py new file mode 100644 index 0000000000..665f5df29e --- /dev/null +++ b/tools/xen/lib/xend/XendRoot.py @@ -0,0 +1,156 @@ +# Copyright (C) 2004 Mike Wray + +"""Xend root class. +Creates the event server and handles configuration. +""" + +import os +import os.path +import sys +import EventServer + +# Initial create of the event server. +eserver = EventServer.instance() + +import sxp + +def reboots(): + """Get a list of system reboots from wtmp. + """ + out = os.popen('/usr/bin/last reboot', 'r') + list = [ x.strip() for x in out if x.startswith('reboot') ] + return list + +def last_reboot(): + """Get the last known system reboot. + """ + l = reboots() + return (l and l[-1]) or None + +class XendRoot: + """Root of the management classes.""" + + lastboot_default = "/var/xen/lastboot" + + """Default path to the root of the database.""" + dbroot_default = "/var/xen/xend-db" + + """Default path to the config file.""" + config_default = "/etc/xen/xend-config.sxp" + + """Environment variable used to override config_default.""" + config_var = "XEND_CONFIG" + + def __init__(self): + self.rebooted = 0 + self.last_reboot = None + self.dbroot = None + self.config_path = None + self.config = None + self.configure() + self.check_lastboot() + eserver.subscribe('xend.*', self.event_handler) + #eserver.subscribe('xend.domain.created', self.event_handler) + #eserver.subscribe('xend.domain.died', self.event_handler) + + def start(self): + eserver.inject('xend.start', self.rebooted) + + def event_handler(self, event, val): + print >> sys.stderr, "EVENT>", event, val + + def read_lastboot(self): + try: + val = file(self.lastboot, 'rb').readlines()[0] + except StandardError, ex: + print 'warning: Error reading', self.lastboot, ex + val = None + return val + + def write_lastboot(self, val): + if not val: return + try: + fdir = os.path.dirname(self.lastboot) + if not os.path.isdir(fdir): + os.makedirs(fdir) + out = file(self.lastboot, 'wb+') + out.write(val) + out.close() + except IOError, ex: + print 'warning: Error writing', self.lastboot, ex + pass + + def check_lastboot(self): + """Check if there has been a system reboot since we saved lastboot. + """ + last_val = self.read_lastboot() + this_val = last_reboot() + if this_val == last_val: + self.rebooted = 0 + else: + self.rebooted = 1 + self.write_lastboot(this_val) + self.last_reboot = this_val + + def get_last_reboot(self): + return self.last_reboot + + def get_rebooted(self): + return self.rebooted + + def configure(self): + self.set_config() + self.dbroot = self.get_config_value("dbroot", self.dbroot_default) + self.lastboot = self.get_config_value("lastboot", self.lastboot_default) + + def get_dbroot(self): + """Get the path to the database root. + """ + return self.dbroot + + def set_config(self): + """If the config file exists, read it. If not, ignore it. + + The config file is a sequence of sxp forms. + """ + self.config_path = os.getenv(self.config_var, self.config_default) + if os.path.exists(self.config_path): + fin = file(self.config_path, 'rb') + try: + config = sxp.parse(fin) + config.insert(0, 'config') + self.config = config + finally: + fin.close() + else: + self.config = ['config'] + + def get_config(self, name=None): + """Get the configuration element with the given name, or + the whole configuration if no name is given. + + name element name (optional) + returns config or none + """ + if name is None: + val = self.config + else: + val = sxp.child(self.config, name) + return val + + def get_config_value(self, name, val=None): + """Get the value of an atomic configuration element. + + name element name + val default value (optional, defaults to None) + returns value + """ + return sxp.child_value(self.config, name, val=val) + +def instance(): + global inst + try: + inst + except: + inst = XendRoot() + return inst diff --git a/tools/xen/lib/xend/XendVnet.py b/tools/xen/lib/xend/XendVnet.py new file mode 100644 index 0000000000..213408e111 --- /dev/null +++ b/tools/xen/lib/xend/XendVnet.py @@ -0,0 +1,69 @@ +# Copyright (C) 2004 Mike Wray + +"""Handler for vnet operations. +""" + +import sxp +import XendDB + +class XendVnet: + """Index of all vnets. Singleton. + """ + + dbpath = "vnet" + + def __init__(self): + # Table of vnet info indexed by vnet id. + self.vnet = {} + self.db = XendDB.XendDB(self.dbpath) + self.vnet = self.db.fetchall("") + + def vnet_ls(self): + """List all vnets. + """ + return self.vnet.keys() + + def vnets(self): + return self.vnet.values() + + def vnet_get(self, id): + """Get a vnet. + + id vnet id + """ + return self.vnet.get(id) + + def vnet_create(self, info): + """Create a vnet. + + info config + """ + self.vnet_configure(info) + + def vnet_configure(self, info): + """Configure a vnet. + id vnet id + info config + """ + # Need to configure for real. + # Only sync if succeeded - otherwise need to back out. + self.vnet[info.id] = info + self.db.save(info.id, info) + + def vnet_delete(self, id): + """Delete a vnet. + + id vnet id + """ + # Need to delete for real. What if fails? + if id in self.vnet: + del self.vnet[id] + self.db.delete(id) + +def instance(): + global inst + try: + inst + except: + inst = XendVnet() + return inst diff --git a/tools/xen/lib/xend/__init__.py b/tools/xen/lib/xend/__init__.py new file mode 100644 index 0000000000..8d1c8b69c3 --- /dev/null +++ b/tools/xen/lib/xend/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/xen/lib/xend/encode.py b/tools/xen/lib/xend/encode.py new file mode 100644 index 0000000000..38c9351db7 --- /dev/null +++ b/tools/xen/lib/xend/encode.py @@ -0,0 +1,165 @@ +# Copyright (C) 2004 Mike Wray +"""Encoding for arguments to HTTP calls. + Uses the url-encoding with MIME type 'application/x-www-form-urlencoded' + if the data does not include files. Otherwise it uses the encoding with + MIME type 'multipart/form-data'. See the HTML4 spec for details. + + """ +import sys +import types +from StringIO import StringIO + +import urllib +import httplib +import random +import md5 + +# Extract from HTML4 spec. +## The following example illustrates "multipart/form-data" +## encoding. Suppose we have the following form: + +##
+##

+## What is your name?
+## What files are you sending?
+## +##

+ +## If the user enters "Larry" in the text input, and selects the text +## file "file1.txt", the user agent might send back the following data: + +## Content-Type: multipart/form-data; boundary=AaB03x + +## --AaB03x +## Content-Disposition: form-data; name="submit-name" + +## Larry +## --AaB03x +## Content-Disposition: form-data; name="files"; filename="file1.txt" +## Content-Type: text/plain + +## ... contents of file1.txt ... +## --AaB03x-- + +## If the user selected a second (image) file "file2.gif", the user agent +## might construct the parts as follows: + +## Content-Type: multipart/form-data; boundary=AaB03x + +## --AaB03x +## Content-Disposition: form-data; name="submit-name" + +## Larry +## --AaB03x +## Content-Disposition: form-data; name="files" +## Content-Type: multipart/mixed; boundary=BbC04y + +## --BbC04y +## Content-Disposition: file; filename="file1.txt" +## Content-Type: text/plain + +## ... contents of file1.txt ... +## --BbC04y +## Content-Disposition: file; filename="file2.gif" +## Content-Type: image/gif +## Content-Transfer-Encoding: binary + +## ...contents of file2.gif... +## --BbC04y-- +## --AaB03x-- + +__all__ = ['encode_data', 'encode_multipart', 'encode_form', 'mime_boundary' ] + +def data_values(d): + if isinstance(d, types.DictType): + return d.items() + else: + return d + +def encode_data(d): + """Encode some data for HTTP transport. + The encoding used is stored in 'Content-Type' in the headers. + + d data - sequence of tuples or dictionary + returns a 2-tuple of the headers and the encoded data + """ + val = ({}, None) + if d is None: return val + multipart = 0 + for (k, v) in data_values(d): + if encode_isfile(v): + multipart = 1 + break + if multipart: + val = encode_multipart(d) + else: + val = encode_form(d) + return val + +def encode_isfile(v): + if isinstance(v, types.FileType): + return 1 + if hasattr(v, 'readlines'): + return 1 + return 0 + +def encode_multipart(d): + boundary = mime_boundary() + hdr = { 'Content-Type': 'multipart/form-data; boundary=' + boundary } + out = StringIO() + for (k,v) in data_values(d): + out.write('--') + out.write(boundary) + out.write('\r\n') + if encode_isfile(v): + out.write('Content-Disposition: form-data; name="') + out.write(k) + if hasattr(v, 'name'): + out.write('"; filename="') + out.write(v.name) + out.write('"\r\n') + out.write('Content-Type: application/octet-stream\r\n') + out.write('\r\n') + for l in v.readlines(): + out.write(l) + else: + out.write('Content-Disposition: form-data; name="') + out.write(k) + out.write('"\r\n') + out.write('\r\n') + out.write(str(v)) + out.write('\r\n') + out.write('--') + out.write(boundary) + out.write('--') + out.write('\r\n') + return (hdr, out.getvalue()) + +def mime_boundary(): + random.seed() + m = md5.new() + for i in range(0, 10): + c = chr(random.randint(1, 255)) + m.update(c) + b = m.hexdigest() + return b[0:16] + +def encode_form(d): + hdr = { 'Content-Type': 'application/x-www-form-urlencoded' } + val = urllib.urlencode(d) + return (hdr, val) + +def main(): + #d = {'a': 1, 'b': 'x y', 'c': file('conf.sxp') } + #d = {'a': 1, 'b': 'x y' } + d = [ ('a', 1), ('b', 'x y'), ('c', file('conf.sxp')) ] + #d = [ ('a', 1), ('b', 'x y')] + v = encode_data(d) + print v[0] + sys.stdout.write(v[1]) + print + +if __name__ == "__main__": + main() diff --git a/tools/xen/lib/xend/server/SrvBase.py b/tools/xen/lib/xend/server/SrvBase.py new file mode 100644 index 0000000000..bcff1bc3a0 --- /dev/null +++ b/tools/xen/lib/xend/server/SrvBase.py @@ -0,0 +1,137 @@ +# Copyright (C) 2004 Mike Wray + +import cgi + +import os +import sys +import types +import StringIO + +from twisted.internet import defer +from twisted.internet import reactor +from twisted.web import error +from twisted.web import resource +from twisted.web import server + +from xen.xend import sxp +from xen.xend import PrettyPrint + +def uri_pathlist(p): + """Split a path into a list. + p path + return list of path elements + """ + l = [] + for x in p.split('/'): + if x == '': continue + l.append(x) + return l + +class SrvBase(resource.Resource): + """Base class for services. + """ + + def parse_form(self, req, method): + """Parse the data for a request, GET using the URL, POST using encoded data. + Posts should use enctype='multipart/form-data' in the
tag, + rather than 'application/x-www-form-urlencoded'. Only 'multipart/form-data' + handles file upload. + + req request + returns a cgi.FieldStorage instance + """ + env = {} + env['REQUEST_METHOD'] = method + if self.query: + env['QUERY_STRING'] = self.query + val = cgi.FieldStorage(fp=req.rfile, headers=req.headers, environ=env) + return val + + def use_sxp(self, req): + """Determine whether to send an SXP response to a request. + Uses SXP if there is no User-Agent, no Accept, or application/sxp is in Accept. + + req request + returns 1 for SXP, 0 otherwise + """ + ok = 0 + user_agent = req.getHeader('User-Agent') + accept = req.getHeader('Accept') + if (not user_agent) or (not accept) or (accept.find(sxp.mime_type) >= 0): + ok = 1 + return ok + + def get_op_method(self, op): + """Get the method for an operation. + For operation 'foo' looks for 'op_foo'. + + op operation name + returns method or None + """ + op_method_name = 'op_' + op + return getattr(self, op_method_name, None) + + def perform(self, req): + """General operation handler for posted operations. + For operation 'foo' looks for a method op_foo and calls + it with op_foo(op, req). Replies with code 500 if op_foo + is not found. + + The method must return a list when req.use_sxp is true + and an HTML string otherwise (or list). + Methods may also return a Deferred (for incomplete processing). + + req request + """ + op = req.args.get('op') + if op is None or len(op) != 1: + req.setResponseCode(404, "Invalid") + return '' + op = op[0] + op_method = self.get_op_method(op) + if op_method is None: + req.setResponseCode(501, "Not implemented") + req.setHeader("Content-Type", "text/plain") + req.write("Not implemented: " + op) + return '' + else: + val = op_method(op, req) + if isinstance(val, defer.Deferred): + val.addCallback(self._cb_perform, req, 1) + return server.NOT_DONE_YET + else: + self._cb_perform(val, req, 0) + return '' + + def _cb_perform(self, val, req, dfr): + """Callback to complete the request. + May be called from a Deferred. + """ + if isinstance(val, error.ErrorPage): + req.write(val.render(req)) + elif self.use_sxp(req): + req.setHeader("Content-Type", sxp.mime_type) + sxp.show(val, req) + else: + req.write('') + self.print_path(req) + if isinstance(val, types.ListType): + req.write('
')
+                PrettyPrint.prettyprint(val, out=req)
+                req.write('
') + else: + req.write(str(val)) + req.write('') + if dfr: + req.finish() + + def print_path(self, req): + """Print the path with hyperlinks. + """ + pathlist = [x for x in req.prepath if x != '' ] + s = "/" + req.write('

/') + for x in pathlist: + s += x + "/" + req.write(' %s/' % (s, x)) + req.write("

") diff --git a/tools/xen/lib/xend/server/SrvConsole.py b/tools/xen/lib/xend/server/SrvConsole.py new file mode 100644 index 0000000000..59d0e5f11c --- /dev/null +++ b/tools/xen/lib/xend/server/SrvConsole.py @@ -0,0 +1,42 @@ +# Copyright (C) 2004 Mike Wray + +from xen.xend import sxp +from xen.xend import XendConsole +from SrvDir import SrvDir + +class SrvConsole(SrvDir): + """An individual console. + """ + + def __init__(self, info): + SrvDir.__init__(self) + self.info = info + self.xc = XendConsole.instance() + + def op_disconnect(self, op, req): + val = self.xc.console_disconnect(self.info.id) + return val + + def render_POST(self, req): + return self.perform(req) + + def render_GET(self, req): + if self.use_sxp(req): + req.setHeader("Content-Type", sxp.mime_type) + sxp.show(self.info.sxpr(), out=req) + else: + req.write('') + self.print_path(req) + #self.ls() + req.write('

%s

' % self.info) + req.write('

Connect to domain %d

' + % (self.info.uri(), self.info.dom2)) + self.form(req) + req.write('') + return '' + + def form(self, req): + req.write('' % req.prePathURL()) + if self.info.connection(): + req.write('') + req.write('') diff --git a/tools/xen/lib/xend/server/SrvConsoleDir.py b/tools/xen/lib/xend/server/SrvConsoleDir.py new file mode 100644 index 0000000000..814b448370 --- /dev/null +++ b/tools/xen/lib/xend/server/SrvConsoleDir.py @@ -0,0 +1,59 @@ +# Copyright (C) 2004 Mike Wray + +from SrvDir import SrvDir +from SrvConsole import SrvConsole +from xen.xend import XendConsole +from xen.xend import sxp + +class SrvConsoleDir(SrvDir): + """Console directory. + """ + + def __init__(self): + SrvDir.__init__(self) + self.xconsole = XendConsole.instance() + + def console(self, x): + val = None + try: + info = self.xconsole.console_get(x) + val = SrvConsole(info) + except KeyError, ex: + print 'SrvConsoleDir>', ex + pass + return val + + def get(self, x): + v = SrvDir.get(self, x) + if v is not None: + return v + v = self.console(x) + return v + + def render_GET(self, req): + if self.use_sxp(req): + req.setHeader("Content-Type", sxp.mime_type) + self.ls_console(req, 1) + else: + req.write("") + self.print_path(req) + self.ls(req) + self.ls_console(req) + #self.form(req.wfile) + req.write("") + return '' + + def ls_console(self, req, use_sxp=0): + url = req.prePathURL() + if not url.endswith('/'): + url += '/' + if use_sxp: + consoles = self.xconsole.console_ls() + sxp.show(consoles, out=req) + else: + consoles = self.xconsole.consoles() + consoles.sort(lambda x, y: cmp(x.id, y.id)) + req.write('
    ') + for c in consoles: + req.write('
  • %s
  • ' % (url, c.id, c)) + req.write('
') diff --git a/tools/xen/lib/xend/server/SrvDaemon.py b/tools/xen/lib/xend/server/SrvDaemon.py new file mode 100644 index 0000000000..385588b86f --- /dev/null +++ b/tools/xen/lib/xend/server/SrvDaemon.py @@ -0,0 +1,704 @@ +########################################################### +## Xen controller daemon +## Copyright (c) 2004, K A Fraser (University of Cambridge) +## Copyright (C) 2004, Mike Wray +########################################################### + +import os +import os.path +import signal +import sys +import socket +import pwd +import re +import StringIO + +from twisted.internet import pollreactor +pollreactor.install() + +from twisted.internet import reactor +from twisted.internet import protocol +from twisted.internet import abstract +from twisted.internet import defer + +from xen.ext import xu + +from xen.xend import sxp +from xen.xend import PrettyPrint +from xen.xend import EventServer +eserver = EventServer.instance() + +from xen.xend.server import SrvServer + +import channel +import blkif +import netif +import console +import domain +from params import * + +DEBUG = 1 + +class MgmtProtocol(protocol.DatagramProtocol): + """Handler for the management socket (unix-domain). + """ + + def __init__(self, daemon): + #protocol.DatagramProtocol.__init__(self) + self.daemon = daemon + + def write(self, data, addr): + return self.transport.write(data, addr) + + def datagramReceived(self, data, addr): + if DEBUG: print 'datagramReceived> addr=', addr, 'data=', data + io = StringIO.StringIO(data) + try: + vals = sxp.parse(io) + res = self.dispatch(vals[0]) + self.send_result(addr, res) + except SystemExit: + raise + except: + if DEBUG: + raise + else: + self.send_error(addr) + + def send_reply(self, addr, sxpr): + io = StringIO.StringIO() + sxp.show(sxpr, out=io) + io.seek(0) + self.write(io.getvalue(), addr) + + def send_result(self, addr, res): + + def fn(res, self=self, addr=addr): + self.send_reply(addr, ['ok', res]) + + if isinstance(res, defer.Deferred): + res.addCallback(fn) + else: + fn(res) + + def send_error(self, addr): + (extype, exval) = sys.exc_info()[:2] + self.send_reply(addr, ['err', + ['type', str(extype) ], + ['value', str(exval) ] ] ) + + def opname(self, name): + """Get the name of the method for an operation. + """ + return 'op_' + name.replace('.', '_') + + def operror(self, name, v): + """Default operation handler - signals an error. + """ + raise NotImplementedError('Invalid operation: ' +name) + + def dispatch(self, req): + """Dispatch a request to its handler. + """ + op_name = sxp.name(req) + op_method_name = self.opname(op_name) + op_method = getattr(self, op_method_name, self.operror) + return op_method(op_name, req) + + def op_console_create(self, name, req): + """Create a new control interface - console for a domain. + """ + print name, req + dom = sxp.child_value(req, 'domain') + if not dom: raise ValueError('Missing domain') + dom = int(dom) + console_port = sxp.child_value(req, 'console_port') + if console_port: + console_port = int(console_port) + resp = self.daemon.console_create(dom, console_port) + print name, resp + return resp + + def op_consoles(self, name, req): + """Get a list of the consoles. + """ + return self.daemon.consoles() + + def op_console_disconnect(self, name, req): + id = sxp.child_value(req, 'id') + if not id: + raise ValueError('Missing console id') + id = int(id) + console = self.daemon.get_console(id) + if not console: + raise ValueError('Invalid console id') + if console.conn: + console.conn.loseConnection() + return ['ok'] + + def op_blkifs(self, name, req): + pass + + def op_blkif_devs(self, name, req): + pass + + def op_blkif_create(self, name, req): + pass + + def op_blkif_dev_create(self, name, req): + pass + + def op_netifs(self, name, req): + pass + + def op_netif_devs(self, name, req): + pass + + def op_netif_create(self, name, req): + pass + + def op_netif_dev_create(self, name, req): + pass + +class NotifierProtocol(protocol.Protocol): + """Asynchronous handler for i/o on the notifier (event channel). + """ + + def __init__(self, channelFactory): + self.channelFactory = channelFactory + + def notificationReceived(self, idx, type): + #print 'NotifierProtocol>notificationReceived>', idx, type + channel = self.channelFactory.getChannel(idx) + if not channel: + return + #print 'NotifierProtocol>notificationReceived> channel', channel + channel.notificationReceived(type) + + def connectionLost(self, reason=None): + pass + + def doStart(self): + pass + + def doStop(self): + pass + + def startProtocol(self): + pass + + def stopProtocol(self): + pass + +class NotifierPort(abstract.FileDescriptor): + """Transport class for the event channel. + """ + + def __init__(self, daemon, notifier, proto, reactor=None): + assert isinstance(proto, NotifierProtocol) + abstract.FileDescriptor.__init__(self, reactor) + self.daemon = daemon + self.notifier = notifier + self.protocol = proto + + def startListening(self): + self._bindNotifier() + self._connectToProtocol() + + def stopListening(self): + if self.connected: + result = self.d = defer.Deferred() + else: + result = None + self.loseConnection() + return result + + def fileno(self): + return self.notifier.fileno() + + def _bindNotifier(self): + self.connected = 1 + + def _connectToProtocol(self): + self.protocol.makeConnection(self) + self.startReading() + + def loseConnection(self): + if self.connected: + self.stopReading() + self.disconnecting = 1 + reactor.callLater(0, self.connectionLost) + + def connectionLost(self, reason=None): + abstract.FileDescriptor.connectionLost(self, reason) + if hasattr(self, 'protocol'): + self.protocol.doStop() + self.connected = 0 + #self.notifier.close() # Not implemented. + os.close(self.fileno()) + del self.notifier + if hasattr(self, 'd'): + self.d.callback(None) + del self.d + + def doRead(self): + #print 'NotifierPort>doRead>', self + count = 0 + while 1: + #print 'NotifierPort>doRead>', count + notification = self.notifier.read() + if not notification: + break + (idx, type) = notification + self.protocol.notificationReceived(idx, type) + self.notifier.unmask(idx) + count += 1 + #print 'NotifierPort>doRead<' + +class EventProtocol(protocol.Protocol): + """Asynchronous handler for a connected event socket. + """ + + def __init__(self, daemon): + #protocol.Protocol.__init__(self) + self.daemon = daemon + # Event queue. + self.queue = [] + # Subscribed events. + self.events = [] + self.parser = sxp.Parser() + self.pretty = 0 + + # For debugging subscribe to everything and make output pretty. + self.subscribe(['*']) + self.pretty = 1 + + def dataReceived(self, data): + try: + self.parser.input(data) + if self.parser.ready(): + val = self.parser.get_val() + res = self.dispatch(val) + self.send_result(res) + if self.parser.at_eof(): + self.loseConnection() + except SystemExit: + raise + except: + if DEBUG: + raise + else: + self.send_error() + + def loseConnection(self): + if self.transport: + self.transport.loseConnection() + if self.connected: + reactor.callLater(0, self.connectionLost) + + def connectionLost(self, reason=None): + self.unsubscribe() + + def send_reply(self, sxpr): + io = StringIO.StringIO() + if self.pretty: + PrettyPrint.prettyprint(sxpr, out=io) + else: + sxp.show(sxpr, out=io) + print >> io + io.seek(0) + return self.transport.write(io.getvalue()) + + def send_result(self, res): + return self.send_reply(['ok', res]) + + def send_error(self): + (extype, exval) = sys.exc_info()[:2] + return self.send_reply(['err', + ['type', str(extype)], + ['value', str(exval)]]) + + def send_event(self, val): + return self.send_reply(['event', val[0], val[1]]) + + def unsubscribe(self): + for event in self.events: + eserver.unsubscribe(event, self.queue_event) + + def subscribe(self, events): + self.unsubscribe() + for event in events: + eserver.subscribe(event, self.queue_event) + self.events = events + + def queue_event(self, name, v): + # Despite the name we dont' queue the event here. + # We send it because the transport will queue it. + self.send_event([name, v]) + + def opname(self, name): + return 'op_' + name.replace('.', '_') + + def operror(self, name, req): + raise NotImplementedError('Invalid operation: ' +name) + + def dispatch(self, req): + op_name = sxp.name(req) + op_method_name = self.opname(op_name) + op_method = getattr(self, op_method_name, self.operror) + return op_method(op_name, req) + + def op_help(self, name, req): + def nameop(x): + if x.startswith('op_'): + return x[3:].replace('_', '.') + else: + return x + + l = [ nameop(k) for k in dir(self) if k.startswith('op_') ] + return l + + def op_quit(self, name, req): + self.loseConnection() + + def op_exit(self, name, req): + sys.exit(0) + + def op_pretty(self, name, req): + self.pretty = 1 + return ['ok'] + + def op_console_disconnect(self, name, req): + id = sxp.child_value(req, 'id') + if not id: + raise ValueError('Missing console id') + self.daemon.console_disconnect(id) + return ['ok'] + + def op_info(self, name, req): + val = ['info'] + val += self.daemon.consoles() + val += self.daemon.blkifs() + val += self.daemon.netifs() + return val + + def op_sys_subscribe(self, name, v): + # (sys.subscribe event*) + # Subscribe to the events: + self.subscribe(v[1:]) + return ['ok'] + + def op_sys_inject(self, name, v): + # (sys.inject event) + event = v[1] + eserver.inject(sxp.name(event), event) + return ['ok'] + + +class EventFactory(protocol.Factory): + """Asynchronous handler for the event server socket. + """ + protocol = EventProtocol + service = None + + def __init__(self, daemon): + #protocol.Factory.__init__(self) + self.daemon = daemon + + def buildProtocol(self, addr): + proto = self.protocol(self.daemon) + proto.factory = self + return proto + +class VirqClient: + def __init__(self, daemon): + self.daemon = daemon + + def virqReceived(self, virq): + print 'VirqClient.virqReceived>', virq + eserver.inject('xend.virq', virq) + + def lostChannel(self, channel): + print 'VirqClient.lostChannel>', channel + +class Daemon: + """The xend daemon. + """ + def __init__(self): + self.shutdown = 0 + + def daemon_pids(self): + pids = [] + pidex = '(?P\d+)' + pythonex = '(?P\S*python\S*)' + cmdex = '(?P.*)' + procre = re.compile('^\s*' + pidex + '\s*' + pythonex + '\s*' + cmdex + '$') + xendre = re.compile('^/usr/sbin/xend\s*(start|restart)\s*.*$') + procs = os.popen('ps -e -o pid,args 2>/dev/null') + for proc in procs: + pm = procre.match(proc) + if not pm: continue + xm = xendre.match(pm.group('cmd')) + if not xm: continue + #print 'pid=', pm.group('pid'), 'cmd=', pm.group('cmd') + pids.append(int(pm.group('pid'))) + return pids + + def new_cleanup(self, kill=0): + err = 0 + pids = self.daemon_pids() + if kill: + for pid in pids: + print "Killing daemon pid=%d" % pid + os.kill(pid, signal.SIGHUP) + elif pids: + err = 1 + print "Daemon already running: ", pids + return err + + def cleanup(self, kill=False): + # No cleanup to do if PID_FILE is empty. + if not os.path.isfile(PID_FILE) or not os.path.getsize(PID_FILE): + return 0 + # Read the pid of the previous invocation and search active process list. + pid = open(PID_FILE, 'r').read() + lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines() + for line in lines: + if re.search('^ *' + pid + '.+xend', line): + if not kill: + print "Daemon is already running (pid %d)" % int(pid) + return 1 + # Old daemon is still active: terminate it. + os.kill(int(pid), 1) + # Delete the stale PID_FILE. + os.remove(PID_FILE) + return 0 + + def install_child_reaper(self): + #signal.signal(signal.SIGCHLD, self.onSIGCHLD) + # Ensure that zombie children are automatically reaped. + xu.autoreap() + + def onSIGCHLD(self, signum, frame): + code = 1 + while code > 0: + code = os.waitpid(-1, os.WNOHANG) + + def start(self): + if self.cleanup(kill=False): + return 1 + + # Detach from TTY. + if not DEBUG: + os.setsid() + + if self.set_user(): + return 1 + + self.install_child_reaper() + + # Fork -- parent writes PID_FILE and exits. + pid = os.fork() + if pid: + # Parent + pidfile = open(PID_FILE, 'w') + pidfile.write(str(pid)) + pidfile.close() + return 0 + # Child + logfile = self.open_logfile() + self.redirect_output(logfile) + self.run() + return 0 + + def open_logfile(self): + if not os.path.exists(CONTROL_DIR): + os.makedirs(CONTROL_DIR) + + # Open log file. Truncate it if non-empty, and request line buffering. + if os.path.isfile(LOG_FILE): + os.rename(LOG_FILE, LOG_FILE+'.old') + logfile = open(LOG_FILE, 'w+', 1) + return logfile + + def set_user(self): + # Set the UID. + try: + os.setuid(pwd.getpwnam(USER)[2]) + return 0 + except KeyError, error: + print "Error: no such user '%s'" % USER + return 1 + + def redirect_output(self, logfile): + if DEBUG: return + # Close down standard file handles + try: + os.close(0) # stdin + os.close(1) # stdout + os.close(2) # stderr + except: + pass + # Redirect output to log file. + sys.stdout = sys.stderr = logfile + + def stop(self): + return self.cleanup(kill=True) + + def run(self): + self.createFactories() + self.listenMgmt() + self.listenEvent() + self.listenNotifier() + self.listenVirq() + SrvServer.create(bridge=1) + reactor.run() + + def createFactories(self): + self.channelF = channel.channelFactory() + self.domainCF = domain.DomainControllerFactory() + self.blkifCF = blkif.BlkifControllerFactory() + self.netifCF = netif.NetifControllerFactory() + self.consoleCF = console.ConsoleControllerFactory() + + def listenMgmt(self): + protocol = MgmtProtocol(self) + s = os.path.join(CONTROL_DIR, MGMT_SOCK) + if os.path.exists(s): + os.unlink(s) + return reactor.listenUNIXDatagram(s, protocol) + + def listenEvent(self): + protocol = EventFactory(self) + return reactor.listenTCP(EVENT_PORT, protocol) + + def listenNotifier(self): + protocol = NotifierProtocol(self.channelF) + p = NotifierPort(self, self.channelF.notifier, protocol, reactor) + p.startListening() + return p + + def listenVirq(self): + virqChan = self.channelF.virqChannel(channel.VIRQ_DOM_EXC) + virqChan.registerClient(VirqClient(self)) + + def exit(self): + reactor.diconnectAll() + sys.exit(0) + + def blkif_set_control_domain(self, dom, recreate=0): + """Set the block device backend control domain. + """ + return self.blkifCF.setControlDomain(dom, recreate=recreate) + + def blkif_get_control_domain(self, dom): + """Get the block device backend control domain. + """ + return self.blkifCF.getControlDomain() + + def blkif_create(self, dom, recreate=0): + """Create a block device interface controller. + + Returns Deferred + """ + d = self.blkifCF.createInstance(dom, recreate=recreate) + return d + + def blkifs(self): + return [ x.sxpr() for x in self.blkifCF.getInstances() ] + + def blkif_get(self, dom): + return self.blkifCF.getInstanceByDom(dom) + + def blkif_dev(self, dom, vdev): + return self.blkifCF.getDomainDevice(dom, vdev) + + def blkif_dev_create(self, dom, vdev, mode, segment, recreate=0): + """Create a block device. + + Returns Deferred + """ + ctrl = self.blkifCF.getInstanceByDom(dom) + if not ctrl: + raise ValueError('No blkif controller: %d' % dom) + print 'blkif_dev_create>', dom, vdev, mode, segment + d = ctrl.attachDevice(vdev, mode, segment, recreate=recreate) + return d + + def netif_set_control_domain(self, dom, recreate=0): + """Set the network interface backend control domain. + """ + return self.netifCF.setControlDomain(dom, recreate=recreate) + + def netif_get_control_domain(self, dom): + """Get the network interface backend control domain. + """ + return self.netifCF.getControlDomain() + + def netif_create(self, dom, recreate=0): + """Create a network interface controller. + + """ + return self.netifCF.createInstance(dom, recreate=recreate) + + def netifs(self): + return [ x.sxpr() for x in self.netifCF.getInstances() ] + + def netif_get(self, dom): + return self.netifCF.getInstanceByDom(dom) + + def netif_dev_create(self, dom, vif, vmac, recreate=0): + """Create a network device. + + todo + """ + ctrl = self.netifCF.getInstanceByDom(dom) + if not ctrl: + raise ValueError('No netif controller: %d' % dom) + d = ctrl.attachDevice(vif, vmac, recreate=recreate) + return d + + def netif_dev(self, dom, vif): + return self.netifCF.getDomainDevice(dom, vif) + + def console_create(self, dom, console_port=None): + """Create a console for a domain. + """ + console = self.consoleCF.getInstanceByDom(dom) + if console is None: + console = self.consoleCF.createInstance(dom, console_port) + return console.sxpr() + + def consoles(self): + return [ c.sxpr() for c in self.consoleCF.getInstances() ] + + def get_console(self, id): + return self.consoleCF.getInstance(id) + + def get_domain_console(self, dom): + return self.consoleCF.getInstanceByDom(dom) + + def console_disconnect(self, id): + """Disconnect any connected console client. + """ + console = self.get_console(id) + if not console: + raise ValueError('Invalid console id') + console.disconnect() + + def domain_shutdown(self, dom, reason): + """Shutdown a domain. + """ + ctrl = self.domainCF.getInstanceByDom(dom) + if not ctrl: + raise ValueError('No domain controller: %d' % dom) + ctrl.shutdown(reason) + return 0 + +def instance(): + global inst + try: + inst + except: + inst = Daemon() + return inst diff --git a/tools/xen/lib/xend/server/SrvDeviceDir.py b/tools/xen/lib/xend/server/SrvDeviceDir.py new file mode 100644 index 0000000000..52f428540d --- /dev/null +++ b/tools/xen/lib/xend/server/SrvDeviceDir.py @@ -0,0 +1,9 @@ +# Copyright (C) 2004 Mike Wray + +from SrvDir import SrvDir + +class SrvDeviceDir(SrvDir): + """Device directory. + """ + + pass diff --git a/tools/xen/lib/xend/server/SrvDir.py b/tools/xen/lib/xend/server/SrvDir.py new file mode 100644 index 0000000000..c49c0b36ba --- /dev/null +++ b/tools/xen/lib/xend/server/SrvDir.py @@ -0,0 +1,91 @@ +# Copyright (C) 2004 Mike Wray + +from twisted.web import error +from xen.xend import sxp +from SrvBase import SrvBase + +class SrvConstructor: + """Delayed constructor for sub-servers. + Does not import the sub-server class or create the object until needed. + """ + + def __init__(self, klass): + """Create a constructor. It is assumed that the class + should be imported as 'import klass from klass'. + + klass name of its class + """ + self.klass = klass + self.obj = None + + def getobj(self): + """Get the sub-server object, importing its class and instantiating it if + necessary. + """ + if not self.obj: + exec 'from %s import %s' % (self.klass, self.klass) + klassobj = eval(self.klass) + self.obj = klassobj() + return self.obj + +class SrvDir(SrvBase): + """Base class for directory servlets. + """ + isLeaf = False + + def __init__(self): + SrvBase.__init__(self) + self.table = {} + self.order = [] + + def getChild(self, x, req): + if x == '': return self + val = self.get(x) + if val is None: + return error.NoResource('Not found') + else: + return val + + def get(self, x): + val = self.table.get(x) + if val is not None: + val = val.getobj() + return val + + def add(self, x, xclass = None): + if xclass is None: + xclass = 'SrvDir' + self.table[x] = SrvConstructor(xclass) + self.order.append(x) + + def render_GET(self, req): + if self.use_sxp(req): + req.setHeader("Content-type", sxp.mime_type) + self.ls(req, 1) + else: + req.write('') + self.print_path(req) + self.ls(req) + self.form(req) + req.write('') + return '' + + def ls(self, req, use_sxp=0): + url = req.prePathURL() + if not url.endswith('/'): + url += '/' + if use_sxp: + req.write('(ls ') + for k in self.order: + req.write(' ' + k) + req.write(')') + else: + req.write('
    ') + for k in self.order: + v = self.get(k) + req.write('
  • %s
  • ' + % (url, k, k)) + req.write('
') + + def form(self, req): + pass diff --git a/tools/xen/lib/xend/server/SrvDomain.py b/tools/xen/lib/xend/server/SrvDomain.py new file mode 100644 index 0000000000..c3684f242b --- /dev/null +++ b/tools/xen/lib/xend/server/SrvDomain.py @@ -0,0 +1,202 @@ +# Copyright (C) 2004 Mike Wray + +from xen.xend import sxp +from xen.xend import XendDomain +from xen.xend import XendConsole +from xen.xend import PrettyPrint +from xen.xend.Args import FormFn + +from SrvDir import SrvDir + +class SrvDomain(SrvDir): + """Service managing a single domain. + """ + + def __init__(self, dom): + SrvDir.__init__(self) + self.dom = dom + self.xd = XendDomain.instance() + self.xconsole = XendConsole.instance() + + def op_unpause(self, op, req): + val = self.xd.domain_unpause(self.dom.id) + return val + + def op_pause(self, op, req): + val = self.xd.domain_pause(self.dom.id) + return val + + def op_shutdown(self, op, req): + val = self.xd.domain_shutdown(self.dom.id) + req.setResponseCode(202) + req.setHeader("Location", "%s/.." % req.prePathURL()) + return val + + def op_destroy(self, op, req): + val = self.xd.domain_destroy(self.dom.id) + req.setHeader("Location", "%s/.." % req.prePathURL()) + return val + + def op_save(self, op, req): + fn = FormFn(self.xd.domain_save, + [['dom', 'int'], + ['file', 'str']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def op_restore(self, op, req): + fn = FormFn(self.xd.domain_restore, + [['dom', 'int'], + ['file', 'str']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def op_migrate(self, op, req): + fn = FormFn(self.xd.domain_migrate, + [['dom', 'int'], + ['destination', 'str']]) + val = fn(req.args, {'dom': self.dom.id}) + val = 0 # Some migrate id. + req.setResponseCode(202) + #req.send_header("Location", "%s/.." % self.path) # Some migrate url. + return val + + def op_pincpu(self, op, req): + fn = FormFn(self.xd.domain_migrate, + [['dom', 'int'], + ['cpu', 'int']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def op_cpu_bvt_set(self, op, req): + fn = FormFn(self.xd.domain_cpu_bvt_set, + [['dom', 'int'], + ['mcuadv', 'int'], + ['warp', 'int'], + ['warpl', 'int'], + ['warpu', 'int']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def op_cpu_atropos_set(self, op, req): + fn = FormFn(self.xd.domain_cpu_atropos_set, + [['dom', 'int'], + ['period', 'int'], + ['slice', 'int'], + ['latency', 'int'], + ['xtratime', 'int']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def op_vifs(self, op, req): + return self.xd.domain_vif_ls(self.dom.id) + + def op_vif(self, op, req): + fn = FormFn(self.xd.domain_vif_get, + [['dom', 'int'], + ['vif', 'int']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def op_vif_stats(self, op, req): + #todo + fn = FormFn(self.xd.domain_vif_stats, + [['dom', 'int'], + ['vif', 'int']]) + #val = fn(req.args, {'dom': self.dom.id}) + val = 999 + #return val + return val + + def op_vif_ip_add(self, op, req): + fn = FormFn(self.xd.domain_vif_ip_add, + [['dom', 'int'], + ['vif', 'int'], + ['ip', 'str']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def op_vif_scheduler_set(self, op, req): + fn = FormFn(self.xd.domain_vif_scheduler_set, + [['dom', 'int'], + ['vif', 'int'], + ['bytes', 'int'], + ['usecs', 'int']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def op_vif_scheduler_get(self, op, req): + fn = FormFn(self.xd.domain_vif_scheduler_set, + [['dom', 'int'], + ['vif', 'int']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def op_vbds(self, op, req): + return self.xd.domain_vbd_ls(self.dom.id) + + def op_vbd(self, op, req): + fn = FormFn(self.xd.domain_vbd_get, + [['dom', 'int'], + ['vbd', 'int']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def op_vbd_add(self, op, req): + fn = FormFn(self.xd.domain_vbd_add, + [['dom', 'int'], + ['uname', 'str'], + ['dev', 'str'], + ['mode', 'str']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def op_vbd_remove(self, op, req): + fn = FormFn(self.xd.domain_vbd_remove, + [['dom', 'int'], + ['dev', 'str']]) + val = fn(req.args, {'dom': self.dom.id}) + return val + + def render_POST(self, req): + return self.perform(req) + + def render_GET(self, req): + op = req.args.get('op') + if op and op[0] in ['vifs', 'vif', 'vif_stats', 'vbds', 'vbd']: + return self.perform(req) + if self.use_sxp(req): + req.setHeader("Content-Type", sxp.mime_type) + sxp.show(self.dom.sxpr(), out=req) + else: + req.write('') + self.print_path(req) + #self.ls() + req.write('

%s

' % self.dom) + if self.dom.console: + cinfo = self.dom.console + cid = cinfo.id + #todo: Local xref: need to know server prefix. + req.write('

Console %s

' + % (cid, cid)) + req.write('

Connect to console

' + % cinfo.uri()) + if self.dom.config: + req.write("
")
+                PrettyPrint.prettyprint(self.dom.config, out=req)
+                req.write("
") + req.write('vif 0 stats' + % req.prePathURL()) + self.form(req) + req.write('') + return '' + + def form(self, req): + req.write('
' % req.prePathURL()) + req.write('') + req.write('') + req.write('') + req.write('') + req.write('
') + req.write('To: ') + req.write('
') diff --git a/tools/xen/lib/xend/server/SrvDomainDir.py b/tools/xen/lib/xend/server/SrvDomainDir.py new file mode 100644 index 0000000000..cb5d8e38cd --- /dev/null +++ b/tools/xen/lib/xend/server/SrvDomainDir.py @@ -0,0 +1,131 @@ +# Copyright (C) 2004 Mike Wray + +from StringIO import StringIO + +from twisted.protocols import http +from twisted.web import error + +from xen.xend import sxp +from xen.xend import XendDomain + +from SrvDir import SrvDir +from SrvDomain import SrvDomain + +class SrvDomainDir(SrvDir): + """Service that manages the domain directory. + """ + + def __init__(self): + SrvDir.__init__(self) + self.xd = XendDomain.instance() + + def domain(self, x): + val = None + try: + dom = self.xd.domain_get(x) + val = SrvDomain(dom) + except KeyError, ex: + print 'SrvDomainDir>', ex + pass + return val + + def get(self, x): + v = SrvDir.get(self, x) + if v is not None: + return v + v = self.domain(x) + return v + + def op_create(self, op, req): + ok = 0 + try: + configstring = req.args.get('config')[0] + print 'config:', configstring + pin = sxp.Parser() + pin.input(configstring) + pin.input_eof() + config = pin.get_val() + ok = 1 + except Exception, ex: + print 'op_create>', ex + if not ok: + req.setResponseCode(http.BAD_REQUEST, "Invalid configuration") + return "Invalid configuration" + return error.ErrorPage(http.BAD_REQUEST, + "Invalid", + "Invalid configuration") + try: + deferred = self.xd.domain_create(config) + deferred.addCallback(self._cb_op_create, configstring, req) + return deferred + except Exception, ex: + raise + #return ['err', str(ex) ] + #req.setResponseCode(http.BAD_REQUEST, "Error creating domain") + #return str(ex) + #return error.ErrorPage(http.BAD_REQUEST, + # "Error creating domain", + # str(ex)) + + + def _cb_op_create(self, dominfo, configstring, req): + """Callback to handle deferred domain creation. + """ + dom = dominfo.id + domurl = "%s/%s" % (req.prePathURL(), dom) + req.setResponseCode(201, "created") + req.setHeader("Location", domurl) + if self.use_sxp(req): + return dominfo.sxpr() + else: + out = StringIO() + print >> out, ('

Created Domain %s

' + % (domurl, dom)) + print >> out, '

'
+            print >> out, configstring
+            print >> out, '

' + val = out.getvalue() + out.close() + return val + + def render_POST(self, req): + return self.perform(req) + + def render_GET(self, req): + if self.use_sxp(req): + req.setHeader("Content-Type", sxp.mime_type) + self.ls_domain(req, 1) + else: + req.write("") + self.print_path(req) + self.ls(req) + self.ls_domain(req) + self.form(req) + req.write("") + return '' + + def ls_domain(self, req, use_sxp=0): + url = req.prePathURL() + if not url.endswith('/'): + url += '/' + if use_sxp: + domains = self.xd.domain_ls() + sxp.show(domains, out=req) + else: + domains = self.xd.domains() + domains.sort(lambda x, y: cmp(x.id, y.id)) + req.write('
    ') + for d in domains: + req.write('
  • Domain %s' + % (url, d.id, d.id)) + req.write('name=%s' % d.name) + req.write('memory=%d'% d.memory) + req.write('
  • ') + req.write('
') + + def form(self, req): + req.write('
' + % req.prePathURL()) + req.write('') + req.write('Config
') + req.write('
') diff --git a/tools/xen/lib/xend/server/SrvEventDir.py b/tools/xen/lib/xend/server/SrvEventDir.py new file mode 100644 index 0000000000..02871a426a --- /dev/null +++ b/tools/xen/lib/xend/server/SrvEventDir.py @@ -0,0 +1,41 @@ +# Copyright (C) 2004 Mike Wray + +from xen.xend import sxp +from xen.xend import EventServer +from SrvDir import SrvDir + +class SrvEventDir(SrvDir): + """Event directory. + """ + + def __init__(self): + SrvDir.__init__(self) + self.eserver = EventServer.instance() + + def op_inject(self, op, req): + eventstring = req.args.get('event') + pin = sxp.Parser() + pin.input(eventstring) + pin.input_eof() + sxpr = pin.get_val() + self.eserver.inject(sxp.name(sxpr), sxpr) + if req.use_sxp: + sxp.name(sxpr) + else: + return '' + eventstring + '' + + def render_POST(self, req): + return self.perform(req) + + def form(self, req): + action = req.prePathURL() + req.write('
' + % action) + req.write('') + req.write('Event
') + req.write('
') + req.write('
' + % action) + req.write('') + req.write('Event file
') + req.write('
') diff --git a/tools/xen/lib/xend/server/SrvNode.py b/tools/xen/lib/xend/server/SrvNode.py new file mode 100644 index 0000000000..69747d80c1 --- /dev/null +++ b/tools/xen/lib/xend/server/SrvNode.py @@ -0,0 +1,54 @@ +# Copyright (C) 2004 Mike Wray + +import os +from SrvDir import SrvDir +from xen.xend import sxp +from xen.xend import XendNode + +class SrvNode(SrvDir): + """Information about the node. + """ + + def __init__(self): + SrvDir.__init__(self) + self.xn = XendNode.instance() + + def op_shutdown(self, op, req): + val = self.xn.shutdown() + return val + + def op_reboot(self, op, req): + val = self.xn.reboot() + return val + + def op_cpu_rrobin_slice_set(self, op, req): + fn = FormFn(self.xn.cpu_rrobin_slice_set, + [['slice', 'int']]) + val = fn(req.args, {}) + return val + + def op_cpu_bvt_slice_set(self, op, req): + fn = FormFn(self.xn.cpu_bvt_slice_set, + [['slice', 'int']]) + val = fn(req.args, {}) + return val + + def render_POST(self, req): + return self.perform(req) + + def render_GET(self, req): + if self.use_sxp(req): + req.setHeader("Content-Type", sxp.mime_type) + sxp.show(['node'] + self.info(), out=req) + else: + req.write('') + self.print_path(req) + req.write('
    ') + for d in self.info(): + req.write('
  • %10s: %s' % (d[0], str(d[1]))) + req.write('
') + req.write('') + return '' + + def info(self): + return self.xn.info() diff --git a/tools/xen/lib/xend/server/SrvRoot.py b/tools/xen/lib/xend/server/SrvRoot.py new file mode 100644 index 0000000000..8d38937b72 --- /dev/null +++ b/tools/xen/lib/xend/server/SrvRoot.py @@ -0,0 +1,30 @@ +# Copyright (C) 2004 Mike Wray + +from xen.xend import XendRoot +xroot = XendRoot.instance() +from SrvDir import SrvDir + +class SrvRoot(SrvDir): + """The root of the xend server. + """ + + """Server sub-components. Each entry is (name, class), where + 'name' is the entry name and 'class' is the name of its class. + """ + #todo Get this list from the XendRoot config. + subdirs = [ + ('node', 'SrvNode' ), + ('domain', 'SrvDomainDir' ), + ('console', 'SrvConsoleDir' ), + ('event', 'SrvEventDir' ), + ('device', 'SrvDeviceDir' ), + ('vnet', 'SrvVnetDir' ), + ] + + def __init__(self): + SrvDir.__init__(self) + for (name, klass) in self.subdirs: + self.add(name, klass) + for (name, klass) in self.subdirs: + self.get(name) + xroot.start() diff --git a/tools/xen/lib/xend/server/SrvServer.py b/tools/xen/lib/xend/server/SrvServer.py new file mode 100644 index 0000000000..6535ad3f37 --- /dev/null +++ b/tools/xen/lib/xend/server/SrvServer.py @@ -0,0 +1,60 @@ +#!/usr/bin/python2 +# Copyright (C) 2004 Mike Wray + +"""Example xend HTTP and console server. + + Can be accessed from a browser or from a program. + Do 'python SrvServer.py' to run the server. + Then point a web browser at http://localhost:8000/xend and follow the links. + Most are stubs, except /domain which has a list of domains and a 'create domain' + button. + + You can also access the server from a program. + Do 'python XendClient.py' to run a few test operations. + + The data served differs depending on the client (as defined by User-Agent + and Accept in the HTTP headers). If the client is a browser, data + is returned in HTML, with interactive forms. If the client is a program, + data is returned in SXP format, with no forms. + + The server serves to the world by default. To restrict it to the local host + change 'interface' in main(). + + Mike Wray +""" +# todo Support security settings etc. in the config file. +# todo Support command-line args. + +from twisted.web import server +from twisted.web import resource +from twisted.internet import reactor + +from xen.xend import XendRoot +xroot = XendRoot.instance() + +from xen.xend import XendBridge + +from SrvRoot import SrvRoot + +def create(port=None, interface=None, bridge=0): + if port is None: port = 8000 + if interface is None: interface = '' + if bridge or xroot.rebooted: + init_bridge() + root = resource.Resource() + xend = SrvRoot() + root.putChild('xend', xend) + site = server.Site(root) + reactor.listenTCP(port, site, interface=interface) + +def init_bridge(): + XendBridge.bridge_create() + XendBridge.reconfigure() + +def main(port=None, interface=None): + create(port, interface) + reactor.run() + + +if __name__ == '__main__': + main() diff --git a/tools/xen/lib/xend/server/SrvVnetDir.py b/tools/xen/lib/xend/server/SrvVnetDir.py new file mode 100644 index 0000000000..a8a814192d --- /dev/null +++ b/tools/xen/lib/xend/server/SrvVnetDir.py @@ -0,0 +1,9 @@ +# Copyright (C) 2004 Mike Wray + +from SrvDir import SrvDir + +class SrvVnetDir(SrvDir): + """Vnet directory. + """ + + pass diff --git a/tools/xen/lib/xend/server/__init__.py b/tools/xen/lib/xend/server/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/tools/xen/lib/xend/server/__init__.py @@ -0,0 +1 @@ + diff --git a/tools/xen/lib/xend/server/blkif.py b/tools/xen/lib/xend/server/blkif.py new file mode 100755 index 0000000000..4e2a49f7d8 --- /dev/null +++ b/tools/xen/lib/xend/server/blkif.py @@ -0,0 +1,341 @@ +from twisted.internet import defer + +from xen.xend import sxp +from xen.xend import PrettyPrint + +import channel +import controller +from messages import * + +class BlkifControllerFactory(controller.ControllerFactory): + """Factory for creating block device interface controllers. + Also handles the 'back-end' channel to the device driver domain. + """ + + def __init__(self): + controller.ControllerFactory.__init__(self) + + self.majorTypes = [ CMSG_BLKIF_BE ] + + self.subTypes = { + CMSG_BLKIF_BE_CREATE : self.recv_be_create, + CMSG_BLKIF_BE_CONNECT : self.recv_be_connect, + CMSG_BLKIF_BE_VBD_CREATE : self.recv_be_vbd_create, + CMSG_BLKIF_BE_VBD_GROW : self.recv_be_vbd_grow, + CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed, + } + self.attached = 1 + self.registerChannel() + + def createInstance(self, dom, recreate=0): + d = self.addDeferred() + blkif = self.getInstanceByDom(dom) + if blkif: + self.callDeferred(blkif) + else: + blkif = BlkifController(self, dom) + self.addInstance(blkif) + if recreate: + self.callDeferred(blkif) + else: + blkif.send_be_create() + return d + + def getDomainDevices(self, dom): + blkif = self.getInstanceByDom(dom) + return (blkif and blkif.getDevices()) or [] + + def getDomainDevice(self, dom, vdev): + blkif = self.getInstanceByDom(dom) + return (blkif and blkif.getDevice(vdev)) or None + + def setControlDomain(self, dom, recreate=0): + if self.dom == dom: return + self.deregisterChannel() + if not recreate: + self.attached = 0 + self.dom = dom + self.registerChannel() + # + #if xend.blkif.be_port: + # xend.blkif.recovery = True + #xend.blkif.be_port = xend.main.port_from_dom(dom) + + def getControlDomain(self): + return self.dom + + def reattachDevice(self, dom, vdev): + blkif = self.getInstanceByDom(dom) + if blkif: + blkif.reattachDevice(vdev) + self.attached = self.devicesAttached() + if self.attached: + self.reattached() + + def devicesAttached(self): + """Check if all devices are attached. + """ + attached = 1 + for blkif in self.getInstances(): + if not blkif.attached: + attached = 0 + break + return attached + + def reattached(self): + for blkif in self.getInstances(): + blkif.reattached() + + def recv_be_create(self, msg, req): + #print 'recv_be_create>' + val = unpackMsg('blkif_be_create_t', msg) + blkif = self.getInstanceByDom(val['domid']) + self.callDeferred(blkif) + + def recv_be_connect(self, msg, req): + #print 'recv_be_create>' + val = unpackMsg('blkif_be_connect_t', msg) + blkif = self.getInstanceByDom(val['domid']) + if blkif: + blkif.send_fe_interface_status_changed() + else: + pass + + def recv_be_vbd_create(self, msg, req): + #print 'recv_be_vbd_create>' + val = unpackMsg('blkif_be_vbd_create_t', msg) + blkif = self.getInstanceByDom(val['domid']) + if blkif: + blkif.send_be_vbd_grow(val['vdevice']) + else: + pass + + def recv_be_vbd_grow(self, msg, req): + #print 'recv_be_vbd_grow>' + val = unpackMsg('blkif_be_vbd_grow_t', msg) + # Check status? + if self.attached: + self.callDeferred(0) + else: + self.reattachDevice(val['domid'], val['vdevice']) + + def recv_be_driver_status_changed(self, msg, req): + val = unpackMsg('blkif_be_driver_status_changed_t', msg) + status = val['status'] + if status == BLKIF_DRIVER_STATUS_UP and not self.attached: + for blkif in self.getInstances(): + blkif.detach() + +class BlkDev(controller.Dev): + """Info record for a block device. + """ + + def __init__(self, ctrl, vdev, mode, segment): + controller.Dev.__init__(self, ctrl) + self.vdev = vdev + self.mode = mode + self.device = segment['device'] + self.start_sector = segment['start_sector'] + self.nr_sectors = segment['nr_sectors'] + self.attached = 1 + + def readonly(self): + return 'w' not in self.mode + + def sxpr(self): + val = ['blkdev', ['vdev', self.vdev], ['mode', self.mode] ] + return val + + def destroy(self): + print 'BlkDev>destroy>', self.vdev + PrettyPrint.prettyprint(self.sxpr()) + self.controller.send_be_vbd_destroy(self.vdev) + +class BlkifController(controller.Controller): + """Block device interface controller. Handles all block devices + for a domain. + """ + + def __init__(self, factory, dom): + #print 'BlkifController> dom=', dom + controller.Controller.__init__(self, factory, dom) + self.devices = {} + + self.majorTypes = [ CMSG_BLKIF_FE ] + + self.subTypes = { + CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED: + self.recv_fe_driver_status_changed, + CMSG_BLKIF_FE_INTERFACE_CONNECT : + self.recv_fe_interface_connect, + } + self.attached = 1 + self.evtchn = None + self.registerChannel() + #print 'BlkifController<', 'dom=', self.dom, 'idx=', self.idx + + def sxpr(self): + val = ['blkif', ['dom', self.dom]] + if self.evtchn: + val.append(['evtchn', + self.evtchn['port1'], + self.evtchn['port2']]) + return val + + def lostChannel(self): + print 'BlkifController>lostChannel>', 'dom=', self.dom + #self.destroyDevices() + controller.Controller.lostChannel(self) + + def getDevices(self): + return self.devices.values() + + def getDevice(self, vdev): + return self.devices.get(vdev) + + def addDevice(self, vdev, mode, segment): + if vdev in self.devices: return None + dev = BlkDev(self, vdev, mode, segment) + self.devices[vdev] = dev + return dev + + def attachDevice(self, vdev, mode, segment, recreate=0): + """Attach a device to the specified interface. + """ + #print 'BlkifController>attach_device>', self.dom, vdev, mode, segment + dev = self.addDevice(vdev, mode, segment) + if not dev: return -1 + if recreate: + d = defer.Deferred() + d.callback(self) + else: + self.send_be_vbd_create(vdev) + d = self.factory.addDeferred() + return d + + def destroy(self): + print 'BlkifController>destroy> dom=', self.dom + def cb_destroy(val): + self.send_be_destroy() + d = self.factory.addDeferred() + d.addCallback(cb_destroy) + self.send_be_disconnect() + #self.destroyDevices() + + def destroyDevices(self): + for dev in self.getDevices(): + dev.destroy() + + def detach(self): + """Detach all devices, when the back-end control domain has changed. + """ + self.attached = 0 + for dev in self.devices.values(): + dev.attached = 0 + self.send_be_vbd_create(vdev) + + def reattachDevice(self, vdev): + """Reattach a device, when the back-end control domain has changed. + """ + dev = self.devices[vdev] + dev.attached = 1 + attached = 1 + for dev in self.devices.values(): + if not dev.attached: + attached = 0 + break + self.attached = attached + return self.attached + + def reattached(self): + """All devices have been reattached after the back-end control + domain has changed. + """ + msg = packMsg('blkif_fe_interface_status_changed_t', + { 'handle' : 0, + 'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED}) + self.writeRequest(msg) + + def recv_fe_driver_status_changed(self, msg, req): + msg = packMsg('blkif_fe_interface_status_changed_t', + { 'handle' : 0, + 'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED, + 'evtchn' : 0 }) + self.writeRequest(msg) + + def recv_fe_interface_connect(self, msg, req): + val = unpackMsg('blkif_fe_interface_connect_t', msg) + self.evtchn = channel.eventChannel(0, self.dom) + print 'recv_fe_interface_connect>' + PrettyPrint.prettyprint(self.sxpr()) + msg = packMsg('blkif_be_connect_t', + { 'domid' : self.dom, + 'blkif_handle' : val['handle'], + 'evtchn' : self.evtchn['port1'], + 'shmem_frame' : val['shmem_frame'] }) + self.factory.writeRequest(msg) + pass + + #def recv_fe_interface_status_changed(self, msg, req): + # (hnd, status, chan) = unpackMsg('blkif_fe_interface_status_changed_t', msg) + # print 'recv_fe_interface_status_changed>', hnd, status, chan + # pass + + def send_fe_interface_status_changed(self): + msg = packMsg('blkif_fe_interface_status_changed_t', + { 'handle' : 0, + 'status' : BLKIF_INTERFACE_STATUS_CONNECTED, + 'evtchn' : self.evtchn['port2'] }) + self.writeRequest(msg) + + def send_be_create(self): + msg = packMsg('blkif_be_create_t', + { 'domid' : self.dom, + 'blkif_handle' : 0 }) + self.factory.writeRequest(msg) + + def send_be_disconnect(self): + print '>BlkifController>send_be_disconnect>', 'dom=', self.dom + msg = packMsg('blkif_be_disconnect_t', + { 'domid' : self.dom, + 'blkif_handle' : 0 }) + self.factory.writeRequest(msg) + + def send_be_destroy(self): + print '>BlkifController>send_be_destroy>', 'dom=', self.dom + msg = packMsg('blkif_be_destroy_t', + { 'domid' : self.dom, + 'blkif_handle' : 0 }) + self.factory.writeRequest(msg) + + def send_be_vbd_create(self, vdev): + dev = self.devices[vdev] + msg = packMsg('blkif_be_vbd_create_t', + { 'domid' : self.dom, + 'blkif_handle' : 0, + 'vdevice' : dev.vdev, + 'readonly' : dev.readonly() }) + self.factory.writeRequest(msg) + + def send_be_vbd_grow(self, vdev): + dev = self.devices[vdev] + msg = packMsg('blkif_be_vbd_grow_t', + { 'domid' : self.dom, + 'blkif_handle' : 0, + 'vdevice' : dev.vdev, + 'extent.device' : dev.device, + 'extent.sector_start' : dev.start_sector, + 'extent.sector_length' : dev.nr_sectors }) + self.factory.writeRequest(msg) + + def send_be_vbd_destroy(self, vdev): + print '>BlkifController>send_be_vbd_destroy>', 'dom=', self.dom, 'vdev=', vdev + PrettyPrint.prettyprint(self.sxpr()) + dev = self.devices[vdev] + msg = packMsg('blkif_be_vbd_destroy_t', + { 'domid' : self.dom, + 'blkif_handle' : 0, + 'vdevice' : dev.vdev }) + del self.devices[vdev] + self.factory.writeRequest(msg) + diff --git a/tools/xen/lib/xend/server/channel.py b/tools/xen/lib/xend/server/channel.py new file mode 100755 index 0000000000..be98a37fd5 --- /dev/null +++ b/tools/xen/lib/xend/server/channel.py @@ -0,0 +1,378 @@ +import xen.ext.xc; xc = xen.ext.xc.new() +from xen.ext import xu +from messages import msgTypeName + +VIRQ_MISDIRECT = 0 # Catch-all interrupt for unbound VIRQs. +VIRQ_TIMER = 1 # Timebase update, and/or requested timeout. +VIRQ_DEBUG = 2 # Request guest to dump debug info. +VIRQ_CONSOLE = 3 # (DOM0) bytes received on emergency console. +VIRQ_DOM_EXC = 4 # (DOM0) Exceptional event for some domain. + +def eventChannel(dom1, dom2): + return xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2) + +class ChannelFactory: + """Factory for creating channels. + Maintains a table of channels. + """ + + """ Channels indexed by index. """ + channels = {} + + def __init__(self): + """Constructor - do not use. Use the channelFactory function.""" + self.notifier = xu.notifier() + + def addChannel(self, channel): + """Add a channel. + """ + idx = channel.idx + self.channels[idx] = channel + self.notifier.bind(idx) + # Try to wake it up + #self.notifier.unmask(idx) + #channel.notify() + + def getChannel(self, idx): + """Get the channel with the given index (if any). + """ + return self.channels.get(idx) + + def delChannel(self, idx): + """Remove the channel with the given index (if any). + """ + if idx in self.channels: + del self.channels[idx] + self.notifier.unbind(idx) + + def domChannel(self, dom): + """Get the channel for the given domain. + Construct if necessary. + """ + dom = int(dom) + for chan in self.channels.values(): + if not isinstance(chan, Channel): continue + if chan.dom == dom: + return chan + chan = Channel(self, dom) + self.addChannel(chan) + return chan + + def virqChannel(self, virq): + """Get the channel for the given virq. + Construct if necessary. + """ + for chan in self.channels.values(): + if not isinstance(chan, VirqChannel): continue + if chan.virq == virq: + return chan + chan = VirqChannel(self, virq) + self.addChannel(chan) + return chan + + def channelClosed(self, channel): + """The given channel has been closed - remove it. + """ + self.delChannel(channel.idx) + + def createPort(self, dom): + """Create a port for a channel to the given domain. + """ + return xu.port(dom) + +def channelFactory(): + """Singleton constructor for the channel factory. + Use this instead of the class constructor. + """ + global inst + try: + inst + except: + inst = ChannelFactory() + return inst + +class BaseChannel: + """Abstract superclass for channels. + + The subclass constructor must set idx to the port to use. + """ + + def __init__(self, factory): + self.factory = factory + self.idx = -1 + self.closed = 0 + + def getIndex(self): + """Get the channel index. + """ + return self.idx + + def notificationReceived(self, type): + """Called when a notification is received. + Closes the channel on error, otherwise calls + handleNotification(type), which should be defined + in a subclass. + """ + #print 'notificationReceived> type=', type, self + if self.closed: return + if type == self.factory.notifier.EXCEPTION: + print 'notificationReceived> EXCEPTION' + info = xc.evtchn_status(self.idx) + if info['status'] == 'unbound': + print 'notificationReceived> EXCEPTION closing...' + self.close() + return + self.handleNotification(type) + + def close(self): + """Close the channel. Calls channelClosed() on the factory. + Override in subclass. + """ + self.factory.channelClosed(self) + + def handleNotification(self, type): + """Handle notification. + Define in subclass. + """ + pass + + +class VirqChannel(BaseChannel): + """A channel for handling a virq. + """ + + def __init__(self, factory, virq): + """Create a channel for the given virq using the given factory. + + Do not call directly, use virqChannel on the factory. + """ + BaseChannel.__init__(self, factory) + self.virq = virq + # Notification port (int). + self.port = xc.evtchn_bind_virq(virq) + self.idx = self.port + # Clients to call when a virq arrives. + self.clients = [] + + def __repr__(self): + return ('' + % (self.virq, self.port)) + + def getVirq(self): + """Get the channel's virq. + """ + return self.virq + + def close(self): + """Close the channel. Calls lostChannel(self) on all its clients and + channelClosed() on the factory. + """ + for c in self.clients: + c.lostChannel(self) + del self.clients + BaseChannel.close(self) + + def registerClient(self, client): + """Register a client. The client will be called with + client.virqReceived(virq) when a virq is received. + The client will be called with client.lostChannel(self) if the + channel is closed. + """ + self.clients.append(client) + + def handleNotification(self, type): + for c in self.clients: + c.virqReceived(self.virq) + + def notify(self): + xc.evtchn_send(self.port) + + +class Channel(BaseChannel): + """A control channel to a domain. Messages for the domain device controllers + are multiplexed over the channel (console, block devs, net devs). + """ + + def __init__(self, factory, dom): + """Create a channel to the given domain using the given factory. + + Do not call directly, use domChannel on the factory. + """ + BaseChannel.__init__(self, factory) + # Domain. + self.dom = int(dom) + # Domain port (object). + self.port = self.factory.createPort(dom) + # Channel port (int). + self.idx = self.port.local_port + # Registered devices. + self.devs = [] + # Devices indexed by the message types they handle. + self.devs_by_type = {} + # Output queue. + self.queue = [] + self.closed = 0 + + def getLocalPort(self): + """Get the local port. + """ + return self.port.local_port + + def getRemotePort(self): + """Get the remote port. + """ + return self.port.remote_port + + def close(self): + """Close the channel. Calls lostChannel() on all its devices and + channelClosed() on the factory. + """ + self.closed = 1 + for d in self.devs: + d.lostChannel() + self.factory.channelClosed(self) + self.devs = [] + self.devs_by_type = {} + + def registerDevice(self, types, dev): + """Register a device controller. + + @param types message types the controller handles + @param dev device controller + """ + if self.closed: return + self.devs.append(dev) + for ty in types: + self.devs_by_type[ty] = dev + + def deregisterDevice(self, dev): + """Remove the registration for a device controller. + + @param dev device controller + """ + if dev in self.devs: + self.devs.remove(dev) + types = [ ty for (ty, d) in self.devs_by_type.items() if d == dev ] + for ty in types: + del self.devs_by_type[ty] + + def getDevice(self, type): + """Get the device controller handling a message type. + + @param type message type + @returns controller or None + """ + return self.devs_by_type.get(type) + + def getMessageType(self, msg): + """Get a 2-tuple of the message type and subtype. + """ + hdr = msg.get_header() + return (hdr['type'], hdr.get('subtype')) + + def __repr__(self): + return ('' + % (self.dom, + self.port.local_port, + self.port.remote_port)) + + def handleNotification(self, type): + work = 0 + work += self.handleRequests() + work += self.handleResponses() + work += self.handleWrites() + if work: + self.notify() + + def notify(self): + self.port.notify() + + def handleRequests(self): + work = 0 + while 1: + msg = self.readRequest() + if not msg: break + self.requestReceived(msg) + work += 1 + return work + + def requestReceived(self, msg): + (ty, subty) = self.getMessageType(msg) + #todo: Must respond before writing any more messages. + #todo: Should automate this (respond on write) + self.port.write_response(msg) + dev = self.getDevice(ty) + if dev: + dev.requestReceived(msg, ty, subty) + else: + print ("requestReceived> No device: Message type %s %d:%d" + % (msgTypeName(ty, subty), ty, subty)), self + + def handleResponses(self): + work = 0 + while 1: + msg = self.readResponse() + if not msg: break + self.responseReceived(msg) + work += 1 + return work + + def responseReceived(self, msg): + (ty, subty) = self.getMessageType(msg) + dev = self.getDevice(ty) + if dev: + dev.responseReceived(msg, ty, subty) + else: + print ("responseReceived> No device: Message type %d:%d" + % (msgTypeName(ty, subty), ty, subty)), self + + def handleWrites(self): + work = 0 + # Pull data from producers. + for dev in self.devs: + work += dev.produceRequests() + # Flush the queue. + while self.queue and self.port.space_to_write_request(): + msg = self.queue.pop(0) + self.port.write_request(msg) + work += 1 + return work + + def writeRequest(self, msg, notify=1): + if self.closed: + val = -1 + elif self.writeReady(): + self.port.write_request(msg) + if notify: self.notify() + val = 1 + else: + self.queue.append(msg) + val = 0 + return val + + def writeResponse(self, msg): + if self.closed: return -1 + self.port.write_response(msg) + return 1 + + def writeReady(self): + if self.closed or self.queue: return 0 + return self.port.space_to_write_request() + + def readRequest(self): + if self.closed: + return None + if self.port.request_to_read(): + val = self.port.read_request() + else: + val = None + return val + + def readResponse(self): + if self.closed: + return None + if self.port.response_to_read(): + val = self.port.read_response() + else: + val = None + return val diff --git a/tools/xen/lib/xend/server/console.py b/tools/xen/lib/xend/server/console.py new file mode 100755 index 0000000000..75793d3bfc --- /dev/null +++ b/tools/xen/lib/xend/server/console.py @@ -0,0 +1,230 @@ + +from twisted.internet import reactor +from twisted.internet import protocol +from twisted.protocols import telnet + +from xen.ext import xu + +from xen.xend import EventServer +eserver = EventServer.instance() + +import controller +from messages import * +from params import * + +"""Telnet binary option.""" +TRANSMIT_BINARY = '0' +WILL = chr(251) +IAC = chr(255) + +class ConsoleProtocol(protocol.Protocol): + """Asynchronous handler for a console TCP socket. + """ + + def __init__(self, controller, idx): + self.controller = controller + self.idx = idx + self.addr = None + self.binary = 0 + + def connectionMade(self): + peer = self.transport.getPeer() + self.addr = (peer.host, peer.port) + if self.controller.connect(self.addr, self): + self.transport.write("Cannot connect to console %d on domain %d\n" + % (self.idx, self.controller.dom)) + self.loseConnection() + return + else: + self.transport.write("Connected to console %d on domain %d\n" + % (self.idx, self.controller.dom)) + self.setTelnetTransmitBinary() + eserver.inject('xend.console.connect', + [self.idx, self.addr[0], self.addr[1]]) + + def setTelnetTransmitBinary(self): + """Send the sequence to set the telnet TRANSMIT-BINARY option. + """ + self.write(IAC + WILL + TRANSMIT_BINARY) + + def dataReceived(self, data): + if self.controller.handleInput(self, data): + self.loseConnection() + + def write(self, data): + #if not self.connected: return -1 + self.transport.write(data) + return len(data) + + def connectionLost(self, reason=None): + eserver.inject('xend.console.disconnect', + [self.idx, self.addr[0], self.addr[1]]) + self.controller.disconnect() + + def loseConnection(self): + self.transport.loseConnection() + +class ConsoleFactory(protocol.ServerFactory): + """Asynchronous handler for a console server socket. + """ + protocol = ConsoleProtocol + + def __init__(self, controller, idx): + #protocol.ServerFactory.__init__(self) + self.controller = controller + self.idx = idx + + def buildProtocol(self, addr): + proto = self.protocol(self.controller, self.idx) + proto.factory = self + return proto + +class ConsoleControllerFactory(controller.ControllerFactory): + """Factory for creating console controllers. + """ + + def createInstance(self, dom, console_port=None): + if console_port is None: + console_port = CONSOLE_PORT_BASE + dom + console = ConsoleController(self, dom, console_port) + self.addInstance(console) + eserver.inject('xend.console.create', + [console.idx, console.dom, console.console_port]) + return console + + def consoleClosed(self, console): + eserver.inject('xend.console.close', console.idx) + self.delInstance(console) + +class ConsoleController(controller.Controller): + """Console controller for a domain. + Does not poll for i/o itself, but relies on the notifier to post console + output and the connected TCP sockets to post console input. + """ + + def __init__(self, factory, dom, console_port): + #print 'ConsoleController> dom=', dom, type(dom) + controller.Controller.__init__(self, factory, dom) + self.majorTypes = [ CMSG_CONSOLE ] + self.status = "new" + self.addr = None + self.conn = None + self.rbuf = xu.buffer() + self.wbuf = xu.buffer() + self.console_port = console_port + + self.registerChannel() + self.listener = None + self.listen() + #print 'ConsoleController<', 'dom=', self.dom, 'idx=', self.idx + + def sxpr(self): + val =['console', + ['status', self.status ], + ['id', self.idx ], + ['domain', self.dom ], + ['local_port', self.channel.getLocalPort() ], + ['remote_port', self.channel.getRemotePort() ], + ['console_port', self.console_port ] ] + if self.addr: + val.append(['connected', self.addr[0], self.addr[1]]) + return val + + def ready(self): + return not (self.closed() or self.rbuf.empty()) + + def closed(self): + return self.status == 'closed' + + def connected(self): + return self.status == 'connected' + + def close(self): + try: + #print 'ConsoleController> close dom=', self.dom + self.status = "closed" + if self.conn: + self.conn.loseConnection() + self.listener.stopListening() + self.deregisterChannel() + self.lostChannel() + except Exception, ex: + print 'ConsoleController>close>', ex + raise + + def listen(self): + """Listen for TCP connections to the console port.. + """ + if self.closed(): return + self.status = "listening" + if self.listener: + #self.listener.startListening() + pass + else: + f = ConsoleFactory(self, self.idx) + self.listener = reactor.listenTCP(self.console_port, f) + + def connect(self, addr, conn): + if self.closed(): return -1 + if self.connected(): return -1 + self.addr = addr + self.conn = conn + self.status = "connected" + self.handleOutput() + return 0 + + def disconnect(self): + if self.conn: + self.conn.loseConnection() + self.addr = None + self.conn = None + self.listen() + + def requestReceived(self, msg, type, subtype): + #print '***Console', self.dom, msg.get_payload() + self.rbuf.write(msg.get_payload()) + self.handleOutput() + + def responseReceived(self, msg, type, subtype): + pass + + def produceRequests(self): + # Send as much pending console data as there is room for. + work = 0 + while not self.wbuf.empty() and self.channel.writeReady(): + msg = xu.message(CMSG_CONSOLE, 0, 0) + msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD)) + work += self.channel.writeRequest(msg, notify=0) + return work + + def handleInput(self, conn, data): + """Handle some external input aimed at the console. + Called from a TCP connection (conn). + """ + if self.closed(): return -1 + if conn != self.conn: return 0 + self.wbuf.write(data) + if self.produceRequests(): + self.channel.notify() + return 0 + + def handleOutput(self): + """Handle buffered output from the console. + Sends it to the connected console (if any). + """ + if self.closed(): + #print 'Console>handleOutput> closed' + return -1 + if not self.conn: + #print 'Console>handleOutput> not connected' + return 0 + while not self.rbuf.empty(): + try: + #print 'Console>handleOutput> writing...' + bytes = self.conn.write(self.rbuf.peek()) + if bytes > 0: + self.rbuf.discard(bytes) + except socket.error, error: + pass + #print 'Console>handleOutput<' + return 0 diff --git a/tools/xen/lib/xend/server/controller.py b/tools/xen/lib/xend/server/controller.py new file mode 100755 index 0000000000..900c2d55b0 --- /dev/null +++ b/tools/xen/lib/xend/server/controller.py @@ -0,0 +1,169 @@ +from twisted.internet import defer + +import channel +from messages import msgTypeName + +class CtrlMsgRcvr: + """Abstract class for things that deal with a control interface to a domain. + """ + + + def __init__(self): + self.channelFactory = channel.channelFactory() + self.majorTypes = [ ] + self.subTypes = {} + self.dom = None + self.channel = None + self.idx = None + + def requestReceived(self, msg, type, subtype): + method = self.subTypes.get(subtype) + if method: + method(msg, 1) + else: + print ('requestReceived> No handler: Message type %s %d:%d' + % (msgTypeName(type, subtype), type, subtype)), self + + def responseReceived(self, msg, type, subtype): + method = self.subTypes.get(subtype) + if method: + method(msg, 0) + else: + print ('responseReceived> No handler: Message type %s %d:%d' + % (msgTypeName(type, subtype), type, subtype)), self + + def lostChannel(self): + pass + + def registerChannel(self): + #print 'CtrlMsgRcvr>registerChannel>', self + self.channel = self.channelFactory.domChannel(self.dom) + self.idx = self.channel.getIndex() + if self.majorTypes: + self.channel.registerDevice(self.majorTypes, self) + + def deregisterChannel(self): + #print 'CtrlMsgRcvr>deregisterChannel>', self + if self.channel: + self.channel.deregisterDevice(self) + del self.channel + + def produceRequests(self): + return 0 + + def writeRequest(self, msg): + if self.channel: + self.channel.writeRequest(msg) + else: + print 'CtrlMsgRcvr>writeRequest>', 'no channel!', self + + def writeResponse(self, msg): + if self.channel: + self.channel.writeResponse(msg) + else: + print 'CtrlMsgRcvr>writeResponse>', 'no channel!', self + +class ControllerFactory(CtrlMsgRcvr): + """Abstract class for factories creating controllers. + Maintains a table of instances. + """ + + def __init__(self): + CtrlMsgRcvr.__init__(self) + self.instances = {} + self.dlist = [] + self.dom = 0 + # Timeout (in seconds) for deferreds. + self.timeout = 10 + + def addInstance(self, instance): + self.instances[instance.idx] = instance + + def getInstance(self, idx): + return self.instances.get(idx) + + def getInstances(self): + return self.instances.values() + + def getInstanceByDom(self, dom): + for inst in self.instances.values(): + if inst.dom == dom: + return inst + return None + + def delInstance(self, instance): + #print 'ControllerFactory>delInstance>', instance.idx + if instance.idx in self.instances: + #print 'ControllerFactory>delInstance> remove', instance.idx + del self.instances[instance.idx] + + def createInstance(self, dom, recreate=0): + raise NotImplementedError() + + def instanceClosed(self, instance): + #print 'ControllerFactory>instanceClosed>', instance.idx, instance + self.delInstance(instance) + + def addDeferred(self): + d = defer.Deferred() + if self.timeout > 0: + # The deferred will error if not called before timeout. + d.setTimeout(self.timeout) + self.dlist.append(d) + return d + + def callDeferred(self, *args): + if self.dlist: + d = self.dlist.pop(0) + d.callback(*args) + + def errDeferred(self, *args): + if self.dlist: + d = self.dlist.pop(0) + d.errback(*args) + +class Controller(CtrlMsgRcvr): + """Abstract class for a device controller attached to a domain. + """ + + def __init__(self, factory, dom): + CtrlMsgRcvr.__init__(self) + self.factory = factory + self.dom = int(dom) + self.channel = None + self.idx = None + + def close(self): + self.deregisterChannel() + self.lostChannel() + + def lostChannel(self): + #print 'Controller>lostChannel>', self, self.factory + self.factory.instanceClosed(self) + +class Dev: + + def __init__(self, controller): + self.controller = controller + self.props = {} + + def setprop(self, k, v): + self.props[k] = v + + def getprop(self, k, v=None): + return self.props.get(k, v) + + def hasprop(self, k): + return k in self.props + + def delprop(self, k): + if k in self.props: + del self.props[k] + + #def __repr__(self): + # return str(self.sxpr()) + + def sxpr(self): + raise NotImplementedError() + + diff --git a/tools/xen/lib/xend/server/cstruct.py b/tools/xen/lib/xend/server/cstruct.py new file mode 100755 index 0000000000..880931b41f --- /dev/null +++ b/tools/xen/lib/xend/server/cstruct.py @@ -0,0 +1,269 @@ +import struct + +class Struct: + + maxDepth = 10 + + base = ['x', 'B', 'H', 'I', 'L', 'Q', 'c', 'h', 'i', 'l', 'q', ] + + sizes = {'B': 1, + 'H': 2, + 'I': 4, + 'L': 4, + 'Q': 8, + 'c': 1, + 'h': 2, + 'i': 4, + 'l': 4, + 'q': 8, + 'x': 1, + } + + formats = { + 'int8' : 'B', + 'int16' : 'H', + 'int32' : 'I', + 'int64' : 'Q', + 'u8' : 'B', + 'u16' : 'H', + 'u32' : 'I', + 'u64' : 'Q' + } + + def typedef(self, name, val): + self.formats[name] = val + + def struct(self, name, *f): + self.typedef(name, StructInfo(self, f)) + + def getType(self, name): + return self.formats[name] + + def format(self, ty): + d = 0 + f = ty + while d < self.maxDepth: + d += 1 + f = self.formats[f] + if isinstance(f, StructInfo): + return f.format() + if f in self.base: + return f + return -1 + + def alignedformat(self, ty): + fmt = self.format(ty) + #print 'alignedformat> %s |%s|' %(ty, fmt) + afmt = self.align(fmt) + #print 'alignedformat< %s |%s| |%s|' % (ty, fmt, afmt) + return afmt + + def align(self, fmt): + n1 = 0 + afmt = '' + for a in fmt: + n2 = self.getSize(a) + m = n1 % n2 + if m: + d = (n2 - m) + afmt += 'x' * d + n1 += d + afmt += a + n1 += n2 + return afmt + + def fmtsize(self, fmt): + s = 0 + for f in fmt: + s += self.getSize(f) + return s + + def getSize(self, f): + return self.sizes[f] + + def pack(self, ty, data): + return self.getType(ty).pack(data) + + def unpack(self, ty, data): + return self.getType(ty).unpack(data) + + def show(self): + l = self.formats.keys() + l.sort() + for v in l: + print "%-35s %-10s %s" % (v, self.format(v), self.alignedformat(v)) + + +class StructInfo: + + def __init__(self, s, f): + self.fmt = None + self.structs = s + self.fields = f + + def alignedformat(self): + if self.afmt: return self.afmt + self.afmt = self.structs.align(self.format()) + return self.afmt + + def format(self): + if self.fmt: return self.fmt + fmt = "" + for (ty, name) in self.fields: + fmt += self.formatString(ty) + self.fmt = fmt + return fmt + + def formatString(self, ty): + if ty in self.fields: + ty = self.fields[ty] + return self.structs.format(ty) + + def pack(self, *args): + return struct.pack(self.alignedformat(), *args) + + def unpack(self, data): + return struct.unpack(self.alignedformat(), data) + +types = Struct() + +types.typedef('short' , 'h') +types.typedef('int' , 'i') +types.typedef('long' , 'l') +types.typedef('unsigned short', 'H') +types.typedef('unsigned int' , 'I') +types.typedef('unsigned long' , 'L') +types.typedef('domid_t' , 'u64') +types.typedef('blkif_vdev_t' , 'u16') +types.typedef('blkif_pdev_t' , 'u16') +types.typedef('blkif_sector_t', 'u64') + +types.struct('u8[6]', + ('u8', 'a1'), + ('u8', 'a2'), + ('u8', 'a3'), + ('u8', 'a4'), + ('u8', 'a5'), + ('u8', 'a6')) + +types.struct('blkif_fe_interface_status_changed_t', + ('unsigned int', 'handle'), + ('unsigned int', 'status'), + ('unsigned int', 'evtchn')) + +types.struct('blkif_fe_driver_status_changed_t', + ('unsigned int', 'status'), + ('unsigned int', 'nr_interfaces')) + +types.struct('blkif_fe_interface_connect_t', + ('unsigned int' , 'handle'), + ('unsigned long', 'shmem_frame')) + +types.struct('blkif_fe_interface_disconnect_t', + ('unsigned int', 'handle')) + +types.struct('blkif_extent_t', + ('blkif_pdev_t' , 'device'), + ('blkif_sector_t', 'sector_start'), + ('blkif_sector_t', 'sector_length')) + +types.struct('blkif_be_create_t', + ('domid_t' , 'domid'), + ('unsigned int', 'blkif_handle'), + ('unsigned int', 'status')) + +types.struct('blkif_be_destroy_t', + ('domid_t' , 'domid'), + ('unsigned int', 'blkif_handle'), + ('unsigned int', 'status')) + +types.struct('blkif_be_connect_t', + ('domid_t' , 'domid'), + ('unsigned int' , 'blkif_handle'), + ('unsigned int' , 'evtchn'), + ('unsigned long', 'shmem_frame'), + ('unsigned int' , 'status')) + +types.struct('blkif_be_disconnect_t', + ('domid_t' , 'domid'), + ('unsigned int', 'blkif_handle'), + ('unsigned int', 'status')) + +types.struct('blkif_be_vbd_create_t', + ('domid_t' , 'domid'), #Q + ('unsigned int', 'blkif_handle'), #I + ('blkif_vdev_t', 'vdevice'), #H + ('int' , 'readonly'), #i + ('unsigned int', 'status')) #I + +types.struct('blkif_be_vbd_destroy_t', + ('domid_t' , 'domid'), + ('unsigned int', 'blkif_handle'), + ('blkif_vdev_t', 'vdevice'), + ('unsigned int', 'status')) + +types.struct('blkif_be_vbd_grow_t', + ('domid_t' , 'domid'), #Q + ('unsigned int' , 'blkif_handle'), #I + ('blkif_vdev_t' , 'vdevice'), #H + ('blkif_extent_t', 'extent'), #HQQ + ('unsigned int' , 'status')) #I + +types.struct('blkif_be_vbd_shrink_t', + ('domid_t' , 'domid'), + ('unsigned int', 'blkif_handle'), + ('blkif_vdev_t', 'vdevice'), + ('unsigned int', 'status')) + +types.struct('blkif_be_driver_status_changed_t', + ('unsigned int', 'status'), + ('unsigned int', 'nr_interfaces')) + +types.struct('netif_fe_interface_status_changed_t', + ('unsigned int', 'handle'), + ('unsigned int', 'status'), + ('unsigned int', 'evtchn'), + ('u8[6]', 'mac')) + +types.struct('netif_fe_driver_status_changed_t', + ('unsigned int', 'status'), + ('unsigned int', 'nr_interfaces')) + +types.struct('netif_fe_interface_connect_t', + ('unsigned int', 'handle'), + ('unsigned long', 'tx_shmem_frame'), + ('unsigned long', 'rx_shmem_frame')) + +types.struct('netif_fe_interface_disconnect_t', + ('unsigned int', 'handle')) + +types.struct('netif_be_create_t', + ('domid_t' , 'domid'), + ('unsigned int', 'netif_handle'), + ('u8[6]' , 'mac'), + ('unsigned int', 'status')) + +types.struct('netif_be_destroy_t', + ('domid_t' , 'domid'), + ('unsigned int', 'netif_handle'), + ('unsigned int', 'status')) + +types.struct('netif_be_connect_t', + ('domid_t' , 'domid'), + ('unsigned int' , 'netif_handle'), + ('unsigned int' , 'evtchn'), + ('unsigned long', 'tx_shmem_frame'), + ('unsigned long', 'rx_shmem_frame'), + ('unsigned int' , 'status')) + +types.struct('netif_be_disconnect_t', + ('domid_t' , 'domid'), + ('unsigned int', 'netif_handle'), + ('unsigned int', 'status')) + +types.struct('netif_be_driver_status_changed_t', + ('unsigned int', 'status'), + ('unsigned int', 'nr_interfaces')) + +if 1 or __name__ == "__main__": + types.show() diff --git a/tools/xen/lib/xend/server/domain.py b/tools/xen/lib/xend/server/domain.py new file mode 100644 index 0000000000..ab22234480 --- /dev/null +++ b/tools/xen/lib/xend/server/domain.py @@ -0,0 +1,41 @@ +import channel +import controller +from messages import * + +class DomainControllerFactory(controller.ControllerFactory): + """Factory for creating domain controllers. + """ + + def createInstance(self, dom): + d = DomainController(self, dom) + self.addInstance(d) + return d + + def getInstanceByDom(self, dom): + for inst in self.instances.values(): + if inst.dom == dom: + return inst + inst = self.createInstance(dom) + return inst + + +class DomainController(controller.Controller): + """Generic controller for a domain. + """ + + reasons = {'poweroff' : 'shutdown_poweroff_t', + 'reboot' : 'shutdown_reboot_t', + 'suspend' : 'shutdown_suspend_t' } + + def __init__(self, factory, dom): + controller.Controller.__init__(self, factory, dom) + self.majorTypes = [ CMSG_SHUTDOWN ] + self.registerChannel() + print 'DomainController>', self, self.channel, self.idx + + def shutdown(self, reason): + msgtype = self.reasons.get(reason) + if not msgtype: + raise ValueError('invalid reason:' + reason) + msg = packMsg(msgtype, {}) + self.writeRequest(msg) diff --git a/tools/xen/lib/xend/server/messages.py b/tools/xen/lib/xend/server/messages.py new file mode 100644 index 0000000000..e12d7b6e24 --- /dev/null +++ b/tools/xen/lib/xend/server/messages.py @@ -0,0 +1,219 @@ +import struct + +from xen.ext import xu + +DEBUG = 0 + +""" All message formats. +Added to incrementally for the various message types. +See below. +""" +msg_formats = {} + +#============================================================================ +# Console message types. +#============================================================================ + +CMSG_CONSOLE = 0 + +console_formats = { 'console_data': (CMSG_CONSOLE, 0) } + +msg_formats.update(console_formats) + +#============================================================================ +# Block interface message types. +#============================================================================ + +CMSG_BLKIF_BE = 1 +CMSG_BLKIF_FE = 2 + +CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED = 0 +CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED = 32 +CMSG_BLKIF_FE_INTERFACE_CONNECT = 33 +CMSG_BLKIF_FE_INTERFACE_DISCONNECT = 34 + +CMSG_BLKIF_BE_CREATE = 0 +CMSG_BLKIF_BE_DESTROY = 1 +CMSG_BLKIF_BE_CONNECT = 2 +CMSG_BLKIF_BE_DISCONNECT = 3 +CMSG_BLKIF_BE_VBD_CREATE = 4 +CMSG_BLKIF_BE_VBD_DESTROY = 5 +CMSG_BLKIF_BE_VBD_GROW = 6 +CMSG_BLKIF_BE_VBD_SHRINK = 7 +CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED = 32 + +BLKIF_DRIVER_STATUS_DOWN = 0 +BLKIF_DRIVER_STATUS_UP = 1 + +BLKIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */ +BLKIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */ +BLKIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */ + +BLKIF_BE_STATUS_OKAY = 0 +BLKIF_BE_STATUS_ERROR = 1 +BLKIF_BE_STATUS_INTERFACE_EXISTS = 2 +BLKIF_BE_STATUS_INTERFACE_NOT_FOUND = 3 +BLKIF_BE_STATUS_INTERFACE_CONNECTED = 4 +BLKIF_BE_STATUS_VBD_EXISTS = 5 +BLKIF_BE_STATUS_VBD_NOT_FOUND = 6 +BLKIF_BE_STATUS_OUT_OF_MEMORY = 7 +BLKIF_BE_STATUS_EXTENT_NOT_FOUND = 8 +BLKIF_BE_STATUS_MAPPING_ERROR = 9 + +blkif_formats = { + 'blkif_be_connect_t': + (CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT), + + 'blkif_be_create_t': + (CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE), + + 'blkif_be_disconnect_t': + (CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT), + + 'blkif_be_destroy_t': + (CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY), + + 'blkif_be_vbd_create_t': + (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE), + + 'blkif_be_vbd_grow_t': + (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW), + + 'blkif_be_vbd_destroy_t': + (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY), + + 'blkif_fe_interface_status_changed_t': + (CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED), + + 'blkif_fe_driver_status_changed_t': + (CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED), + + 'blkif_fe_interface_connect_t': + (CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT), +} + +msg_formats.update(blkif_formats) + +#============================================================================ +# Network interface message types. +#============================================================================ + +CMSG_NETIF_BE = 3 +CMSG_NETIF_FE = 4 + +CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED = 0 +CMSG_NETIF_FE_DRIVER_STATUS_CHANGED = 32 +CMSG_NETIF_FE_INTERFACE_CONNECT = 33 +CMSG_NETIF_FE_INTERFACE_DISCONNECT = 34 + +CMSG_NETIF_BE_CREATE = 0 +CMSG_NETIF_BE_DESTROY = 1 +CMSG_NETIF_BE_CONNECT = 2 +CMSG_NETIF_BE_DISCONNECT = 3 +CMSG_NETIF_BE_DRIVER_STATUS_CHANGED = 32 + +NETIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */ +NETIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */ +NETIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */ + +NETIF_DRIVER_STATUS_DOWN = 0 +NETIF_DRIVER_STATUS_UP = 1 + +netif_formats = { + 'netif_be_connect_t': + (CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT), + + 'netif_be_create_t': + (CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE), + + 'netif_be_disconnect_t': + (CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT), + + 'netif_be_destroy_t': + (CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY), + + 'netif_be_driver_status_changed_t': + (CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED), + + 'netif_fe_driver_status_changed_t': + (CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED), + + 'netif_fe_interface_connect_t': + (CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT), + + 'netif_fe_interface_status_changed_t': + (CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED), + } + +msg_formats.update(netif_formats) + +#============================================================================ +CMSG_SHUTDOWN = 6 + +CMSG_SHUTDOWN_POWEROFF = 0 +CMSG_SHUTDOWN_REBOOT = 1 +CMSG_SHUTDOWN_SUSPEND = 2 + +STOPCODE_shutdown = 0 +STOPCODE_reboot = 1 +STOPCODE_suspend = 2 + +shutdown_formats = { + 'shutdown_poweroff_t': + (CMSG_SHUTDOWN, CMSG_SHUTDOWN_POWEROFF), + + 'shutdown_reboot_t': + (CMSG_SHUTDOWN, CMSG_SHUTDOWN_REBOOT), + + 'shutdown_suspend_t': + (CMSG_SHUTDOWN, CMSG_SHUTDOWN_SUSPEND), + } + +msg_formats.update(shutdown_formats) + +#============================================================================ + +class Msg: + pass + +def packMsg(ty, params): + if DEBUG: print '>packMsg', ty, params + (major, minor) = msg_formats[ty] + args = {} + for (k, v) in params.items(): + if k == 'mac': + for i in range(0, 6): + args['mac[%d]' % i] = v[i] + else: + args[k] = v + if DEBUG: + for (k, v) in args.items(): + print 'packMsg>', k, v, type(v) + msgid = 0 + msg = xu.message(major, minor, msgid, args) + return msg + +def unpackMsg(ty, msg): + args = msg.get_payload() + mac = [0, 0, 0, 0, 0, 0] + macs = [] + for (k, v) in args.items(): + if k.startswith('mac['): + macs += k + i = int(k[4:5]) + mac[i] = v + else: + pass + if macs: + args['mac'] = mac + for k in macs: + del args[k] + if DEBUG: print 'createInstance> dom=', dom + netif = self.getInstanceByDom(dom) + if netif is None: + netif = NetifController(self, dom) + self.addInstance(netif) + return netif + + def getDomainDevices(self, dom): + netif = self.getInstanceByDom(dom) + return (netif and netif.getDevices()) or [] + + def getDomainDevice(self, dom, vif): + netif = self.getInstanceByDom(dom) + return (netif and netif.getDevice(vif)) or None + + def setControlDomain(self, dom, recreate=0): + """Set the 'back-end' device driver domain. + """ + if self.dom == dom: return + self.deregisterChannel() + if not recreate: + self.attached = 0 + self.dom = dom + self.registerChannel() + # + #if xend.netif.be_port.remote_dom != 0: + # xend.netif.recovery = True + # xend.netif.be_port = xend.main.port_from_dom(dom) + # + + def getControlDomain(self): + return self.dom + + def recv_be_create(self, msg, req): + self.callDeferred(0) + + def recv_be_connect(self, msg, req): + val = unpackMsg('netif_be_connect_t', msg) + dom = val['domid'] + vif = val['netif_handle'] + netif = self.getInstanceByDom(dom) + if netif: + netif.send_interface_connected(vif) + else: + print "recv_be_connect> unknown vif=", vif + pass + + def recv_be_driver_status_changed(self, msg, req): + val = unpackMsg('netif_be_driver_status_changed_t', msg) + status = val['status'] + if status == NETIF_DRIVER_STATUS_UP and not self.attached: + # If we are not attached the driver domain was changed, and + # this signals the new driver domain is ready. + for netif in self.getInstances(): + netif.reattach_devices() + self.attached = 1 + +## pl = msg.get_payload() +## status = pl['status'] +## if status == NETIF_DRIVER_STATUS_UP: +## if xend.netif.recovery: +## print "New netif backend now UP, notifying guests:" +## for netif_key in interface.list.keys(): +## netif = interface.list[netif_key] +## netif.create() +## print " Notifying %d" % netif.dom +## msg = xu.message( +## CMSG_NETIF_FE, +## CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0, +## { 'handle' : 0, 'status' : 1 }) +## netif.ctrlif_tx_req(xend.main.port_from_dom(netif.dom),msg) +## print "Done notifying guests" +## recovery = False + +class NetDev(controller.Dev): + """Info record for a network device. + """ + + def __init__(self, ctrl, vif, mac): + controller.Dev.__init__(self, ctrl) + self.vif = vif + self.mac = mac + self.evtchn = None + self.bridge = None + + def sxpr(self): + vif = str(self.vif) + mac = ':'.join(map(lambda x: "%x" % x, self.mac)) + val = ['netdev', ['vif', vif], ['mac', mac]] + if self.bridge: + val.append(['bridge', self.bridge]) + if self.evtchn: + val.append(['evtchn', + self.evtchn['port1'], + self.evtchn['port2']]) + return val + + def bridge_add(self, bridge): + self.bridge = XendBridge.vif_bridge_add(self.controller.dom, self.vif, bridge) + + def bridge_rem(self): + if not self.bridge: return + XendBridge.vif_bridge_rem(self.controller.dom, self.vif, self.bridge) + self.bridge = None + + def destroy(self): + def cb_destroy(val): + self.controller.send_be_destroy(self.vif) + print 'NetDev>destroy>', 'vif=', self.vif + PrettyPrint.prettyprint(self.sxpr()) + self.bridge_rem() + d = self.controller.factory.addDeferred() + d.addCallback(cb_destroy) + self.controller.send_be_disconnect(self.vif) + #self.controller.send_be_destroy(self.vif) + + +class NetifController(controller.Controller): + """Network interface controller. Handles all network devices for a domain. + """ + + def __init__(self, factory, dom): + #print 'NetifController> dom=', dom + controller.Controller.__init__(self, factory, dom) + self.devices = {} + + self.majorTypes = [ CMSG_NETIF_FE ] + + self.subTypes = { + CMSG_NETIF_FE_DRIVER_STATUS_CHANGED: + self.recv_fe_driver_status_changed, + CMSG_NETIF_FE_INTERFACE_CONNECT : + self.recv_fe_interface_connect, + } + self.registerChannel() + #print 'NetifController<', 'dom=', self.dom, 'idx=', self.idx + + def sxpr(self): + val = ['netif', ['dom', self.dom]] + return val + + def randomMAC(self): + # VIFs get a random MAC address with a "special" vendor id. + # + # NB. The vendor is currently an "obsolete" one that used to belong + # to DEC (AA-00-00). Using it is probably a bit rude :-) + # + # NB2. The first bit of the first random octet is set to zero for + # all dynamic MAC addresses. This may allow us to manually specify + # MAC addresses for some VIFs with no fear of clashes. + mac = [ 0xaa, 0x00, 0x00, + random.randint(0x00, 0x7f), + random.randint(0x00, 0xff), + random.randint(0x00, 0xff) ] + return mac + + def lostChannel(self): + print 'NetifController>lostChannel>', 'dom=', self.dom + #self.destroyDevices() + controller.Controller.lostChannel(self) + + def getDevices(self): + return self.devices.values() + + def getDevice(self, vif): + return self.devices.get(vif) + + def addDevice(self, vif, vmac): + if vmac is None: + mac = self.randomMAC() + else: + mac = [ int(x, 16) for x in vmac.split(':') ] + if len(mac) != 6: raise ValueError("invalid mac") + #print "attach_device>", "vif=", vif, "mac=", mac + dev = NetDev(self, vif, mac) + self.devices[vif] = dev + return dev + + def destroy(self): + print 'NetifController>destroy>', 'dom=', self.dom + self.destroyDevices() + + def destroyDevices(self): + for dev in self.getDevices(): + dev.destroy() + + def attachDevice(self, vif, vmac, recreate=0): + """Attach a network device. + If vmac is None a random mac address is assigned. + + @param vif interface index + @param vmac mac address (string) + """ + self.addDevice(vif, vmac) + if recreate: + d = defer.Deferred() + d.callback(self) + else: + d = self.factory.addDeferred() + self.send_be_create(vif) + return d + + def reattach_devices(self): + """Reattach all devices when the back-end control domain has changed. + """ + d = self.factory.addDeferred() + self.send_be_create(vif) + self.attach_fe_devices(0) + + def attach_fe_devices(self): + for dev in self.devices.values(): + msg = packMsg('netif_fe_interface_status_changed_t', + { 'handle' : dev.vif, + 'status' : NETIF_INTERFACE_STATUS_DISCONNECTED, + 'evtchn' : 0, + 'mac' : dev.mac }) + self.writeRequest(msg) + + def recv_fe_driver_status_changed(self, msg, req): + if not req: return + msg = packMsg('netif_fe_driver_status_changed_t', + { 'status' : NETIF_DRIVER_STATUS_UP, + 'nr_interfaces' : len(self.devices) }) + self.writeRequest(msg) + self.attach_fe_devices() + + def recv_fe_interface_connect(self, msg, req): + val = unpackMsg('netif_fe_interface_connect_t', msg) + dev = self.devices[val['handle']] + dev.evtchn = channel.eventChannel(0, self.dom) + msg = packMsg('netif_be_connect_t', + { 'domid' : self.dom, + 'netif_handle' : dev.vif, + 'evtchn' : dev.evtchn['port1'], + 'tx_shmem_frame' : val['tx_shmem_frame'], + 'rx_shmem_frame' : val['rx_shmem_frame'] }) + self.factory.writeRequest(msg) + + def send_interface_connected(self, vif): + dev = self.devices[vif] + msg = packMsg('netif_fe_interface_status_changed_t', + { 'handle' : dev.vif, + 'status' : NETIF_INTERFACE_STATUS_CONNECTED, + 'evtchn' : dev.evtchn['port2'], + 'mac' : dev.mac }) + self.writeRequest(msg) + + def send_be_create(self, vif): + dev = self.devices[vif] + msg = packMsg('netif_be_create_t', + { 'domid' : self.dom, + 'netif_handle' : dev.vif, + 'mac' : dev.mac }) + self.factory.writeRequest(msg) + + def send_be_disconnect(self, vif): + dev = self.devices[vif] + msg = packMsg('netif_be_disconnect_t', + { 'domid' : self.dom, + 'netif_handle' : dev.vif }) + self.factory.writeRequest(msg) + + def send_be_destroy(self, vif): + print 'NetifController>send_be_destroy>', 'dom=', self.dom, 'vif=', vif + PrettyPrint.prettyprint(self.sxpr()) + dev = self.devices[vif] + del self.devices[vif] + msg = packMsg('netif_be_destroy_t', + { 'domid' : self.dom, + 'netif_handle' : vif }) + self.factory.writeRequest(msg) diff --git a/tools/xen/lib/xend/server/params.py b/tools/xen/lib/xend/server/params.py new file mode 100644 index 0000000000..7949f1431f --- /dev/null +++ b/tools/xen/lib/xend/server/params.py @@ -0,0 +1,10 @@ +# The following parameters could be placed in a configuration file. +PID_FILE = '/var/run/xend.pid' +LOG_FILE = '/var/log/xend.log' +USER = 'root' +CONTROL_DIR = '/var/run/xend' +MGMT_SOCK = 'xendsock' # relative to CONTROL_DIR +EVENT_PORT = 8001 + +CONSOLE_PORT_BASE = 9600 + diff --git a/tools/xen/lib/xend/sxp.py b/tools/xen/lib/xend/sxp.py new file mode 100644 index 0000000000..dd4fece6f0 --- /dev/null +++ b/tools/xen/lib/xend/sxp.py @@ -0,0 +1,557 @@ +#!/usr/bin/python2 +# Copyright (C) 2004 Mike Wray +""" +Input-driven parsing for s-expression (sxp) format. +Create a parser: pin = Parser(); +Then call pin.input(buf) with your input. +Call pin.input_eof() when done. +Use pin.read() to see if a value has been parsed, pin.get_val() +to get a parsed value. You can call ready and get_val at any time - +you don't have to wait until after calling input_eof. + +""" +from __future__ import generators + +import sys +import types +import errno +import string + +__all__ = [ + "mime_type", + "ParseError", + "Parser", + "atomp", + "show", + "show_xml", + "elementp", + "name", + "attributes", + "attribute", + "children", + "child", + "child_at", + "child0", + "child1", + "child2", + "child3", + "child4", + "child_value", + "has_id", + "with_id", + "child_with_id", + "elements", + "parse", + ] + +mime_type = "application/sxp" + +escapes = { + 'a': '\a', + 'b': '\b', + 't': '\t', + 'n': '\n', + 'v': '\v', + 'f': '\f', + 'r': '\r', + '\\': '\\', + '\'': '\'', + '\"': '\"'} + +k_list_open = "(" +k_list_close = ")" +k_attr_open = "@" +k_eval = "!" + +escapes_rev = {} +for k in escapes: + escapes_rev[escapes[k]] = k + +class ParseError(StandardError): + + def __init__(self, parser, value): + self.parser = parser + self.value = value + + def __str__(self): + return self.value + +class ParserState: + + def __init__(self, fn, parent=None): + self.parent = parent + self.buf = '' + self.val = [] + self.delim = None + self.fn = fn + + def push(self, fn): + return ParserState(fn, parent=self) + +class Parser: + + def __init__(self): + self.error = sys.stderr + self.reset() + + def reset(self): + self.val = [] + self.eof = 0 + self.err = 0 + self.line_no = 0 + self.char_no = 0 + self.state = None + + def push_state(self, fn): + self.state = self.state.push(fn) + + def pop_state(self): + val = self.state + self.state = self.state.parent + if self.state and self.state.fn == self.state_start: + # Return to start state - produce the value. + self.val += self.state.val + self.state.val = [] + return val + + def in_class(self, c, s): + return s.find(c) >= 0 + + def in_space_class(self, c): + return self.in_class(c, ' \t\n\v\f\r') + + def is_separator(self, c): + return self.in_class(c, '{}()<>[]!;') + + def in_comment_class(self, c): + return self.in_class(c, '#') + + def in_string_quote_class(self, c): + return self.in_class(c, '"\'') + + def in_printable_class(self, c): + return self.in_class(c, string.printable) + + def set_error_stream(self, error): + self.error = error + + def has_error(self): + return self.err > 0 + + def at_eof(self): + return self.eof + + def input_eof(self): + self.eof = 1 + self.input_char(-1) + + def input(self, buf): + if not buf or len(buf) == 0: + self.input_eof() + else: + for c in buf: + self.input_char(c) + + def input_char(self, c): + if self.at_eof(): + pass + elif c == '\n': + self.line_no += 1 + self.char_no = 0 + else: + self.char_no += 1 + + if self.state is None: + self.begin_start(None) + self.state.fn(c) + + def ready(self): + return len(self.val) > 0 + + def get_val(self): + v = self.val[0] + self.val = self.val[1:] + return v + + def get_all(self): + return self.val + + def begin_start(self, c): + self.state = ParserState(self.state_start) + + def end_start(self): + self.val += self.state.val + self.pop_state() + + def state_start(self, c): + if self.at_eof(): + self.end_start() + elif self.in_space_class(c): + pass + elif self.in_comment_class(c): + self.begin_comment(c) + elif c == k_list_open: + self.begin_list(c) + elif c == k_list_close: + raise ParseError(self, "syntax error: "+c) + elif self.in_string_quote_class(c): + self.begin_string(c) + elif self.in_printable_class(c): + self.begin_atom(c) + elif c == chr(4): + # ctrl-D, EOT: end-of-text. + self.input_eof() + else: + raise ParseError(self, "invalid character: code %d" % ord(c)) + + def begin_comment(self, c): + self.push_state(self.state_comment) + self.state.buf += c + + def end_comment(self): + self.pop_state() + + def state_comment(self, c): + if c == '\n' or self.at_eof(): + self.end_comment() + else: + self.state.buf += c + + def begin_string(self, c): + self.push_state(self.state_string) + self.state.delim = c + + def end_string(self): + val = self.state.buf + self.state.parent.val.append(val) + self.pop_state() + + def state_string(self, c): + if self.at_eof(): + raise ParseError(self, "unexpected EOF") + elif c == self.state.delim: + self.end_string() + elif c == '\\': + self.push_state(self.state_escape) + else: + self.state.buf += c + + def state_escape(self, c): + if self.at_eof(): + raise ParseError(self, "unexpected EOF") + d = escapes.get(c) + if d: + self.state.parent.buf += d + self.pop_state() + elif c == 'x': + self.state.fn = self.state_hex + self.state.val = 0 + else: + self.state.fn = self.state_octal + self.state.val = 0 + self.input_char(c) + + def state_octal(self, c): + def octaldigit(c): + self.state.val *= 8 + self.state.val += ord(c) - ord('0') + self.state.buf += c + if self.state.val < 0 or self.state.val > 0xff: + raise ParseError(self, "invalid octal escape: out of range " + self.state.buf) + if len(self.state.buf) == 3: + octaldone() + + def octaldone(): + d = chr(self.state.val) + self.state.parent.buf += d + self.pop_state() + + if self.at_eof(): + raise ParseError(self, "unexpected EOF") + elif '0' <= c <= '7': + octaldigit(c) + elif len(self.buf): + octaldone() + self.input_char(c) + + def state_hex(self, c): + def hexdone(): + d = chr(self.state.val) + self.state.parent.buf += d + self.pop_state() + + def hexdigit(c, d): + self.state.val *= 16 + self.state.val += ord(c) - ord(d) + self.state.buf += c + if self.state.val < 0 or self.state.val > 0xff: + raise ParseError(self, "invalid hex escape: out of range " + self.state.buf) + if len(self.state.buf) == 2: + hexdone() + + if self.at_eof(): + raise ParseError(self, "unexpected EOF") + elif '0' <= c <= '9': + hexdigit(c, '0') + elif 'A' <= c <= 'F': + hexdigit(c, 'A') + elif 'a' <= c <= 'f': + hexdigit(c, 'a') + elif len(buf): + hexdone() + self.input_char(c) + + def begin_atom(self, c): + self.push_state(self.state_atom) + self.state.buf = c + + def end_atom(self): + val = self.state.buf + self.state.parent.val.append(val) + self.pop_state() + + def state_atom(self, c): + if self.at_eof(): + self.end_atom() + elif (self.is_separator(c) or + self.in_space_class(c) or + self.in_comment_class(c)): + self.end_atom() + self.input_char(c) + else: + self.state.buf += c + + def begin_list(self, c): + self.push_state(self.state_list) + + def end_list(self): + val = self.state.val + self.state.parent.val.append(val) + self.pop_state() + + def state_list(self, c): + if self.at_eof(): + raise ParseError(self, "unexpected EOF") + elif c == k_list_close: + self.end_list() + else: + self.state_start(c) + +def atomp(sxpr): + if sxpr.isalnum() or sxpr == '@': + return 1 + for c in sxpr: + if c in string.whitespace: return 0 + if c in '"\'\\(){}[]<>$#&%^': return 0 + if c in string.ascii_letters: continue + if c in string.digits: continue + if c in '.-_:/~': continue + return 0 + return 1 + +def show(sxpr, out=sys.stdout): + if isinstance(sxpr, types.ListType): + out.write(k_list_open) + i = 0 + for x in sxpr: + if i: out.write(' ') + show(x, out) + i += 1 + out.write(k_list_close) + elif isinstance(sxpr, types.StringType) and atomp(sxpr): + out.write(sxpr) + else: + #out.write("'" + str(sxpr) + "'") + out.write(repr(str(sxpr))) + +def show_xml(sxpr, out=sys.stdout): + if isinstance(sxpr, types.ListType): + element = name(sxpr) + out.write('<%s' % element) + for attr in attributes(sxpr): + out.write(' %s=%s' % (attr[0], attr[1])) + out.write('>') + i = 0 + for x in children(sxpr): + if i: out.write(' ') + show_xml(x, out) + i += 1 + out.write('' % element) + elif isinstance(sxpr, types.StringType) and atomp(sxpr): + out.write(sxpr) + else: + out.write(str(sxpr)) + +def elementp(sxpr, elt=None): + return (isinstance(sxpr, types.ListType) + and len(sxpr) + and (None == elt or sxpr[0] == elt)) + +def name(sxpr): + val = None + if isinstance(sxpr, types.StringType): + val = sxpr + elif isinstance(sxpr, types.ListType) and len(sxpr): + val = sxpr[0] + return val + +def attributes(sxpr): + val = [] + if isinstance(sxpr, types.ListType) and len(sxpr) > 1: + attr = sxpr[1] + if elementp(attr, k_attr_open): + val = attr[1:] + return val + +def attribute(sxpr, key, val=None): + for x in attributes(sxpr): + if x[0] == key: + val = x[1] + break + return val + +def children(sxpr, elt=None): + val = [] + if isinstance(sxpr, types.ListType) and len(sxpr) > 1: + i = 1 + x = sxpr[i] + if elementp(x, k_attr_open): + i += 1 + val = sxpr[i : ] + if elt: + def iselt(x): + return elementp(x, elt) + val = filter(iselt, val) + return val + +def child(sxpr, elt, val=None): + for x in children(sxpr): + if elementp(x, elt): + val = x + break + return val + +def child_at(sxpr, index, val=None): + kids = children(sxpr) + if len(kids) > index: + val = kids[index] + return val + +def child0(sxpr, val=None): + return child_at(sxpr, 0, val) + +def child1(sxpr, val=None): + return child_at(sxpr, 1, val) + +def child2(sxpr, val=None): + return child_at(sxpr, 2, val) + +def child3(sxpr, val=None): + return child_at(sxpr, 3, val) + +def child4(sxpr, val=None): + return child_at(sxpr, 4, val) + +def child_value(sxpr, elt, val=None): + kid = child(sxpr, elt) + if kid: + val = child_at(kid, 0, val) + return val + +def has_id(sxpr, id): + """Test if an s-expression has a given id. + """ + return attribute(sxpr, 'id') == id + +def with_id(sxpr, id, val=None): + """Find the first s-expression with a given id, at any depth. + + sxpr s-exp or list + id id + val value if not found (default None) + + return s-exp or val + """ + if isinstance(sxpr, types.ListType): + for n in sxpr: + if has_id(n, id): + val = n + break + v = with_id(n, id) + if v is None: continue + val = v + break + return val + +def child_with_id(sxpr, id, val=None): + """Find the first child with a given id. + + sxpr s-exp or list + id id + val value if not found (default None) + + return s-exp or val + """ + if isinstance(sxpr, types.ListType): + for n in sxpr: + if has_id(n, id): + val = n + break + return val + +def elements(sxpr, ctxt=None): + """Generate elements (at any depth). + Visit elements in pre-order. + Values generated are (node, context) + The context is None if there is no parent, otherwise + (index, parent, context) where index is the node's index w.r.t its parent, + and context is the parent's context. + + sxpr s-exp + + returns generator + """ + yield (sxpr, ctxt) + i = 0 + for n in children(sxpr): + if isinstance(n, types.ListType): + # Calling elements() recursively does not generate recursively, + # it just returns a generator object. So we must iterate over it. + for v in elements(n, (i, sxpr, ctxt)): + yield v + i += 1 + +def parse(io): + """Completely parse all input from 'io'. + + io input file object + returns list of values, None if incomplete + raises ParseError on parse error + """ + pin = Parser() + while 1: + buf = io.readline() + pin.input(buf) + if len(buf) == 0: + break + if pin.ready(): + val = pin.get_all() + else: + val = None + return val + + +if __name__ == '__main__': + print ">main" + pin = Parser() + while 1: + buf = sys.stdin.read(1024) + #buf = sys.stdin.readline() + pin.input(buf) + while pin.ready(): + val = pin.get_val() + print + print '****** val=', val + if len(buf) == 0: + break + diff --git a/tools/xen/lib/xm/__init__.py b/tools/xen/lib/xm/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/xen/lib/xm/create.py b/tools/xen/lib/xm/create.py new file mode 100644 index 0000000000..1def1903f2 --- /dev/null +++ b/tools/xen/lib/xm/create.py @@ -0,0 +1,366 @@ +# Copyright (C) 2004 Mike Wray +"""Domain creation. +""" +import string +import sys + +from xen.xend import sxp +from xen.xend import PrettyPrint +from xen.xend.XendClient import server + +from xen.xend.xm.opts import * + +gopts = Opts(use="""[options] + +Create a domain. +""") + +gopts.opt('help', short='h', + fn=set_true, default=0, + use="Print this help.") + +gopts.opt('quiet', short='q', + fn=set_true, default=0, + use="Quiet.") + +gopts.opt('path', val='PATH', + fn=set_value, default='.:/etc/xen', + use="Search path for default scripts.") + +gopts.opt('defaults', short='f', val='FILE', + fn=set_value, default='xmdefaults', + use="Use the given default script.") + +gopts.opt('config', short='F', val='FILE', + fn=set_value, default=None, + use='Domain configuration to use (SXP).') + +gopts.opt('load', short='L', val='FILE', + fn=set_value, default=None, + use='Domain saved state to load.') + +gopts.opt('define', short='D', val='VAR=VAL', + fn=set_var, default=None, + use="""Set a variable before loading defaults, e.g. '-D vmid=3' + to set vmid. May be repeated to set more thanone variable.""") + +gopts.opt('dryrun', short='n', + fn=set_true, default=0, + use="Dry run - print the config but don't create the domain.") + +gopts.opt('name', short='N', val='NAME', + fn=set_value, default=None, + use="Domain name.") + +gopts.opt('console', short='c', + fn=set_true, default=0, + use="Connect to console after domain is created.") + +gopts.opt('kernel', short='k', val='FILE', + fn=set_value, default=None, + use="Path to kernel image.") + +gopts.opt('ramdisk', short='r', val='FILE', + fn=set_value, default='', + use="Path to ramdisk.") + +gopts.opt('builder', short='b', val='FUNCTION', + fn=set_value, default='linux', + use="Function to use to build the domain.") + +gopts.opt('memory', short='m', val='MEMORY', + fn=set_value, default=128, + use="Domain memory in MB.") + +gopts.opt('blkif', + fn=set_true, default=0, + use="Make the domain a block device backend.") + +gopts.opt('netif', + fn=set_true, default=0, + use="Make the domain a network interface backend.") + +gopts.opt('disk', short='d', val='phy:DEV,VDEV,MODE', + fn=append_value, default=[], + use="""Add a disk device to a domain. The physical device is DEV, which + is exported to the domain as VDEV. The disk is read-only if MODE + is 'r', read-write if MODE is 'w'. + The option may be repeated to add more than one disk. + """) + +gopts.opt('pci', val='BUS,DEV,FUNC', + fn=append_value, default=[], + use="""Add a PCI device to a domain, using given params (in hex). + For example '-pci c0,02,1a'. + The option may be repeated to add more than one pci device. + """) + +gopts.opt('ipaddr', short='i', val="IPADDR", + fn=append_value, default=[], + use="Add an IP address to the domain.") + +gopts.opt('vif', val="mac=MAC,bridge=BRIDGE", + fn=append_value, default=[], + use="""Add a network interface with the given MAC address and bridge. + If mac is not specified a random MAC address is used. + If bridge is not specified the default bridge is used. + This option may be repeated to add more than one vif. + Specifying vifs will increase the number of interfaces as needed. + """) + +gopts.opt('nics', val="NUM", + fn=set_int, default=1, + use="""Set the number of network interfaces. + Use the vif option to define interface parameters, otherwise + defaults are used. Specifying vifs will increase the + number of interfaces as needed. + """) + +gopts.opt('root', short='R', val='DEVICE', + fn=set_value, default='', + use="""Set the root= parameter on the kernel command line. + Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""") + +gopts.opt('extra', short='E', val="ARGS", + fn=set_value, default='', + use="Set extra arguments to append to the kernel command line.") + +gopts.opt('ip', short='I', val='IPADDR', + fn=set_value, default='', + use="Set the kernel IP interface address.") + +gopts.opt('gateway', val="IPADDR", + fn=set_value, default='', + use="Set the kernel IP gateway.") + +gopts.opt('netmask', val="MASK", + fn=set_value, default = '', + use="Set the kernel IP netmask.") + +gopts.opt('hostname', val="NAME", + fn=set_value, default='', + use="Set the kernel IP hostname.") + +gopts.opt('interface', val="INTF", + fn=set_value, default="eth0", + use="Set the kernel IP interface name.") + +gopts.opt('dhcp', val="off|dhcp", + fn=set_value, default='off', + use="Set the kernel dhcp option.") + +gopts.opt('nfs_server', val="IPADDR", + fn=set_value, default=None, + use="Set the address of the NFS server for NFS root.") + +gopts.opt('nfs_root', val="PATH", + fn=set_value, default=None, + use="Set the path of the root NFS directory.") + +def strip(pre, s): + """Strip prefix 'pre' if present. + """ + if s.startswith(pre): + return s[len(pre):] + else: + return s + +def configure_image(config, opts): + """Create the image config. + """ + config_image = [ opts.builder ] + config_image.append([ 'kernel', os.path.abspath(opts.kernel) ]) + if opts.ramdisk: + config_image.append([ 'ramdisk', os.path.abspath(opts.ramdisk) ]) + if opts.cmdline_ip: + cmdline_ip = strip('ip=', opts.cmdline_ip) + config_image.append(['ip', cmdline_ip]) + if opts.root: + cmdline_root = strip('root=', opts.root) + config_image.append(['root', cmdline_root]) + if opts.extra: + config_image.append(['args', opts.extra]) + config.append(['image', config_image ]) + +def configure_disks(config_devs, opts): + """Create the config for disks (virtual block devices). + """ + for (uname, dev, mode) in opts.disk: + config_vbd = ['vbd', + ['uname', uname], + ['dev', dev ], + ['mode', mode ] ] + config_devs.append(['device', config_vbd]) + +def configure_pci(config_devs, opts): + """Create the config for pci devices. + """ + for (bus, dev, func) in opts.pci: + config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]] + config_devs.append(['device', config_pci]) + +def configure_vifs(config_devs, opts): + """Create the config for virtual network interfaces. + """ + vifs = opts.vif + vifs_n = max(opts.nics, len(vifs)) + + for idx in range(0, vifs_n): + if idx < len(vifs): + d = vifs[idx] + mac = d.get('mac') + bridge = d.get('bridge') + else: + mac = None + bridge = None + config_vif = ['vif'] + if mac: + config_vif.append(['mac', mac]) + if bridge: + config_vif.append(['bridge', bridge]) + config_devs.append(['device', config_vif]) + +def configure_vfr(config, opts): + if not opts.ipaddr: return + config_vfr = ['vfr'] + idx = 0 # No way of saying which IP is for which vif? + for ip in opts.ipaddr: + config_vfr.append(['vif', ['id', idx], ['ip', ip]]) + config.append(config_vfr) + + +def make_config(opts): + """Create the domain configuration. + """ + + config = ['vm', + ['name', opts.name ], + ['memory', opts.memory ] ] + if opts.cpu: + config.append(['cpu', opts.cpu]) + if opts.blkif: + config.append(['backend', ['blkif']]) + if opts.netif: + config.append(['backend', ['netif']]) + + configure_image(config, opts) + config_devs = [] + configure_disks(config_devs, opts) + configure_pci(config_devs, opts) + configure_vifs(config_devs, opts) + config += config_devs + return config + +def preprocess_disk(opts): + if not opts.disk: return + disk = [] + for v in opts.disk: + d = v.split(',') + if len(d) != 3: + opts.err('Invalid disk specifier: ' + v) + disk.append(d) + opts.disk = disk + +def preprocess_pci(opts): + if not opts.pci: return + pci = [] + for v in opts.pci: + d = v.split(',') + if len(d) != 3: + opts.err('Invalid pci specifier: ' + v) + # Components are in hex: add hex specifier. + hexd = map(lambda v: '0x'+v, d) + pci.append(hexd) + opts.pci = pci + +def preprocess_vifs(opts): + if not opts.vif: return + vifs = [] + for vif in opts.vif: + d = {} + a = vif.split(',') + for b in a: + (k, v) = b.strip().split('=') + k = k.strip() + v = v.strip() + if k not in ['mac', 'bridge']: + opts.err('Invalid vif specifier: ' + vif) + d[k] = v + vifs.append(d) + opts.vif = vifs + +def preprocess_ip(opts): + setip = (opts.hostname or opts.netmask + or opts.gateway or opts.dhcp or opts.interface) + if not setip: return + #if not opts + ip = (opts.ip + + ':' + + ':' + opts.gateway + + ':' + opts.netmask + + ':' + opts.hostname + + ':' + opts.interface + + ':' + opts.dhcp) + opts.cmdline_ip = ip + +def preprocess_nfs(opts): + if (opts.nfs_root or opts.nfs_server): + if (not opts.nfs_root) or (not opts.nfs_server): + opts.err('Must set nfs root and nfs server') + else: + return + nfs = 'nfsroot=' + opts.nfs_server + ':' + opts.nfs_root + opts.extra = nfs + ' ' + opts.extra + +def preprocess(opts): + if not opts.kernel: + opts.err("No kernel specified") + preprocess_disk(opts) + preprocess_pci(opts) + preprocess_vifs(opts) + preprocess_ip(opts) + preprocess_nfs(opts) + +def make_domain(opts, config): + """Create, build and start a domain. + Returns: [int] the ID of the new domain. + """ + if opts.vals.load: + filename = os.path.abspath(opts.vals.load) + dominfo = server.xend_domain_restore(filename, config) + else: + dominfo = server.xend_domain_create(config) + + dom = int(sxp.child_value(dominfo, 'id')) + console_info = sxp.child(dominfo, 'console') + if console_info: + console_port = int(sxp.child_value(console_info, 'port')) + else: + console_port = None + + if server.xend_domain_unpause(dom) < 0: + server.xend_domain_destroy(dom) + opts.err("Failed to start domain %d" % dom) + opts.info("Started domain %d, console on port %d" + % (dom, console_port)) + return (dom, console_port) + +def main(argv): + opts = gopts + args = opts.parse(argv) + if opts.vals.help: + opts.usage() + return + if opts.vals.config: + pass + else: + opts.load_defaults() + preprocess(opts.vals) + config = make_config(opts.vals) + if opts.vals.dryrun: + PrettyPrint.prettyprint(config) + else: + make_domain(opts, config) + +if __name__ == '__main__': + main(sys.argv) diff --git a/tools/xen/lib/xm/main.py b/tools/xen/lib/xm/main.py new file mode 100644 index 0000000000..d03ceae7f2 --- /dev/null +++ b/tools/xen/lib/xm/main.py @@ -0,0 +1,448 @@ +# Copyright (C) 2004 Mike Wray +"""Grand unified management application for Xen. +""" +import os +import os.path +import sys +from getopt import getopt + +from xen.xend import PrettyPrint +from xen.xend import sxp +from xen.xend.XendClient import server +from xen.xend.xm import create, shutdown + +class Prog: + """Base class for sub-programs. + """ + + """Program group it belongs to""" + group = 'all' + """Program name.""" + name = '??' + """Short program info.""" + info = '' + + def __init__(self, xm): + self.xm = xm + + def err(self, msg): + self.xm.err(msg) + + def help(self, args): + self.shortHelp(args) + + def shortHelp(self, args): + print "%-14s %s" % (self.name, self.info) + + def main(self, args): + """Program main entry point. + """ + pass + + +class ProgUnknown(Prog): + + name = 'unknown' + info = '' + + def help(self, args): + self.xm.err("Unknown command: %s\nTry '%s help' for more information." + % (args[0], self.xm.name)) + + main = help + +class Xm: + """Main application. + """ + + def __init__(self): + self.name = 'xm' + self.unknown = ProgUnknown(self) + self.progs = {} + + def err(self, msg): + print >>sys.stderr, "Error:", msg + sys.exit(1) + + def main(self, args): + """Main entry point. Dispatches to the progs. + """ + self.name = args[0] + if len(args) < 2: + self.err("Missing command\nTry '%s help' for more information." + % self.name) + help = self.helparg(args) + p = self.getprog(args[1], self.unknown) + if help: + p.help(args[1:]) + else: + p.main(args[1:]) + + def helparg(self, args): + for a in args: + if a in ['-h', '--help']: + return 1 + return 0 + + def prog(self, pklass): + """Add a sub-program. + + pklass program class (Prog subclass) + """ + p = pklass(self) + self.progs[p.name] = p + return p + + def getprog(self, name, val=None): + """Get a sub-program. + """ + return self.progs.get(name, val) + + def proglist(self): + """Get a list of sub-programs, ordered by group. + """ + groups = {} + for p in self.progs.values(): + l = groups.get(p.group, []) + l.append(p) + groups[p.group] = l + kl = groups.keys() + kl.sort() + pl = [] + for k in kl: + l = groups[k] + l.sort() + pl += l + return pl + +# Create the application object, then add the sub-program classes. +xm = Xm() + +class ProgHelp(Prog): + + name = "help" + info = "Print help." + + def help(self, args): + if len(args) == 2: + name = args[1] + p = self.xm.getprog(name) + if p: + p.help(args[1:]) + else: + print '%s: Unknown command: %s' % (self.name, name) + else: + for p in self.xm.proglist(): + p.shortHelp(args) + print "\nTry '%s help CMD' for help on CMD" % self.xm.name + + main = help + +xm.prog(ProgHelp) + +class ProgCreate(Prog): + + group = 'domain' + name = "create" + info = """Create a domain.""" + + def help(self, args): + create.main([args[0], '-h']) + + def main(self, args): + create.main(args) + +xm.prog(ProgCreate) + +class ProgSave(Prog): + group = 'domain' + name = "save" + info = """Save domain state (and config) to file.""" + + def help(self, args): + print args[0], "DOM FILE" + print """\nSave domain with id DOM to FILE.""" + + def main(self, args): + if len(args) < 3: self.err("%s: Missing arguments" % args[0]) + dom = args[1] + savefile = os.path.abspath(args[2]) + server.xend_domain_save(dom, savefile) + +xm.prog(ProgSave) + +class ProgRestore(Prog): + group = 'domain' + name = "restore" + info = """Create a domain from a saved state.""" + + def help(self, args): + print args[0], "FILE [CONFIG]" + print "\nRestore a domain from FILE using configuration CONFIG." + + def main(self, help, args): + if len(args) < 2: self.err("%s: Missing arguments" % args[0]) + savefile = os.path.abspath(args[1]) + if len(args) >= 3: + configfile = os.path.abspath(args[2]) + else: + configfile = None + info = server.xend_domain_restore(savefile, configfile) + PrettyPrint.prettyprint(info) + +xm.prog(ProgRestore) + +class ProgList(Prog): + group = 'domain' + name = "list" + info = """List info about domains.""" + + short_options = 'l' + long_options = ['long'] + + def help(self, args): + if help: + print args[0], '[options] [DOM...]' + print """\nGet information about domains. + Either all domains or the domains given. + + -l, --long Get more detailed information. + """ + return + + def main(self, args): + use_long = 0 + (options, params) = getopt(args[1:], + self.short_options, + self.long_options) + n = len(params) + for (k, v) in options: + if k in ['-l', '--long']: + use_long = 1 + + if n == 0: + doms = map(int, server.xend_domains()) + doms.sort() + else: + doms = map(int, params) + + if use_long: + self.long_list(doms) + else: + self.brief_list(doms) + + def brief_list(self, doms): + print 'Dom Name Mem(MB) CPU State Time(s)' + for dom in doms: + info = server.xend_domain(dom) + d = {} + d['dom'] = int(dom) + d['name'] = sxp.child_value(info, 'name', '??') + d['mem'] = int(sxp.child_value(info, 'memory', '0')) + d['cpu'] = int(sxp.child_value(info, 'cpu', '0')) + d['state'] = sxp.child_value(info, 'state', '??') + d['cpu_time'] = float(sxp.child_value(info, 'cpu_time', '0')) + print ("%(dom)-4d %(name)-16s %(mem)7d %(cpu)3d %(state)5s %(cpu_time)7.1f" % d) + + def long_list(self, doms): + for dom in doms: + info = server.xend_domain(dom) + print '\nDomain %d' % dom + PrettyPrint.prettyprint(info) + +xm.prog(ProgList) + +class ProgDestroy(Prog): + group = 'domain' + name = "destroy" + info = """Terminate a domain immediately.""" + + def help(self, args): + print args[0], 'DOM' + print '\nTerminate domain DOM immediately.' + + def main(self, args): + if len(args) < 2: self.err("%s: Missing domain" % args[0]) + dom = args[1] + server.xend_domain_destroy(dom) + +xm.prog(ProgDestroy) + +class ProgShutdown(Prog): + group = 'domain' + name = "shutdown" + info = """Shutdown a domain.""" + + def help(self, args): + shutdown.main([args[0], '-h']) + + def main(self, args): + shutdown.main(args) + +xm.prog(ProgShutdown) + +class ProgPause(Prog): + group = 'domain' + name = "pause" + info = """Pause execution of a domain.""" + + def help(self, args): + print args[0], 'DOM' + print '\nPause execution of domain DOM.' + + def main(self, args): + if len(args) < 2: self.err("%s: Missing domain" % args[0]) + dom = args[1] + server.xend_domain_pause(dom) + +xm.prog(ProgPause) + +class ProgUnpause(Prog): + group = 'domain' + name = "unpause" + info = """Unpause a paused domain.""" + + def help(self, args): + print args[0], 'DOM' + print '\nUnpause execution of domain DOM.' + + def main(self, args): + if len(args) < 2: self.err("%s: Missing domain" % args[0]) + dom = args[1] + server.xend_domain_unpause(dom) + +xm.prog(ProgUnpause) + +class ProgPincpu(Prog): + group = 'domain' + name = "pincpu" + info = """Pin a domain to a cpu. """ + + def help(self, args): + print args[0],'DOM CPU' + print '\nPin domain DOM to cpu CPU.' + + def main(self, args): + if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0]) + v = map(int, args[1:3]) + server.xend_domain_pincpu(*v) + +xm.prog(ProgPincpu) + +class ProgBvt(Prog): + group = 'scheduler' + name = "bvt" + info = """Set BVT scheduler parameters.""" + + def help(self, args): + print args[0], "DOM MCUADV WARP WARPL WARPU" + print '\nSet Borrowed Virtual Time scheduler parameters.' + + def main(self, args): + if len(args) != 6: self.err("%s: Invalid argument(s)" % args[0]) + v = map(int, args[1:6]) + server.xend_domain_cpu_bvt_set(*v) + +xm.prog(ProgBvt) + +class ProgBvtslice(Prog): + group = 'scheduler' + name = "bvtslice" + info = """Set the BVT scheduler slice.""" + + def help(self, args): + print args[0], 'SLICE' + print '\nSet Borrowed Virtual Time scheduler slice.' + + def main(self, args): + if len(args) < 2: self.err('%s: Missing slice' % args[0]) + server.xend_node_cpu_bvt_slice_set(slice) + +xm.prog(ProgBvtslice) + +class ProgAtropos(Prog): + group = 'scheduler' + name= "atropos" + info = """Set atropos parameters.""" + + def help(self, args): + print args[0], "DOM PERIOD SLICE LATENCY XTRATIME" + print "\nSet atropos parameters." + + def main(self, args): + if len(args) != 5: self.err("%s: Invalid argument(s)" % args[0]) + v = map(int, args[1:5]) + server.xend_domain_cpu_atropos_set(*v) + +xm.prog(ProgAtropos) + +class ProgRrobin(Prog): + group = 'scheduler' + name = "rrobin" + info = """Set round robin slice.""" + + def help(self, args): + print args[0], "SLICE" + print "\nSet round robin scheduler slice." + + def main(self, args): + if len(args) != 2: self.err("%s: Invalid argument(s)" % args[0]) + rrslice = int(args[1]) + server.xend_node_rrobin_set(rrslice) + +xm.prog(ProgRrobin) + +class ProgInfo(Prog): + group = 'host' + name = "info" + info = """Get information about the xen host.""" + + def main(self, args): + info = server.xend_node() + for x in info[1:]: + print "%-23s:" % x[0], x[1] + +xm.prog(ProgInfo) + +class ProgConsoles(Prog): + group = 'console' + name = "consoles" + info = """Get information about domain consoles.""" + + def main(self, args): + l = server.xend_consoles() + print "Dom Port Id" + for x in l: + info = server.xend_console(x) + d = {} + d['dom'] = sxp.child(info, 'dst', ['dst', '?', '?'])[1] + d['port'] = sxp.child_value(info, 'port', '?') + d['id'] = sxp.child_value(info, 'id', '?') + print "%(dom)3s %(port)4s %(id)3s" % d + +xm.prog(ProgConsoles) + +class ProgConsole(Prog): + group = 'console' + name = "console" + info = """Open a console to a domain.""" + + def help(self, args): + print "console DOM" + print "\nOpen a console to domain DOM." + + def main(self, args): + if len(args) < 2: self.err("%s: Missing domain" % args[0]) + dom = args[1] + info = server.xend_domain(dom) + console = sxp.child(info, "console") + if not console: + self.err("No console information") + port = sxp.child_value(console, "port") + from xenctl import console_client + console_client.connect("localhost", int(port)) + +xm.prog(ProgConsole) + +def main(args): + xm.main(args) diff --git a/tools/xen/lib/xm/opts.py b/tools/xen/lib/xm/opts.py new file mode 100644 index 0000000000..5b9515215d --- /dev/null +++ b/tools/xen/lib/xm/opts.py @@ -0,0 +1,339 @@ +# Copyright (C) 2004 Mike Wray +"""Object-oriented command-line option support. +""" +from getopt import getopt +import os +import os.path +import sys +import types + +class Opt: + """An individual option. + """ + def __init__(self, opts, name, short=None, long=None, + val=None, fn=None, use=None, default=None): + """Create an option. + + opts parent options object + name name of the field it controls + short short (1-char) command line switch (optional) + long long command-line switch. Defaults to option name. + val string used to print option args in help. + If val is not specified the option has no arg. + fn function to call when the option is specified. + use usage (help) string + default default value if not specified on command-line + """ + self.opts = opts + self.name = name + self.short = short + if long is None: + long = name + self.long = long + self.val = val + self.use = use + self.default = default + self.optkeys = [] + if self.short: + self.optkeys.append('-' + self.short) + if self.long: + self.optkeys.append('--' + self.long) + self.fn = fn + self.specified_opt = None + self.specified_val = None + self.value = None + self.set(default) + + def __repr__(self): + return self.name + '=' + str(self.specified_val) + + __str__ = __repr__ + + def set(self, value): + """Set the option value. + """ + self.opts.setopt(self.name, value) + + def get(self): + """Get the option value. + """ + return self.opts.getopt(self.name) + + def append(self, value): + """Append a value to the option value. + """ + v = self.get() or [] + v.append(value) + self.set(v) + + def short_opt(self): + """Short option spec. + """ + if self.short: + if self.val: + return self.short + ':' + else: + return self.short + else: + return None + + def long_opt(self): + """Long option spec. + """ + if self.long: + if self.val: + return self.long + '=' + else: + return self.long + else: + return None + + def show(self): + sep = '' + for x in self.optkeys: + print sep, x, + sep = ',' + if self.val: + print self.val, + print + if self.use: + print '\t', + print self.use + if self.val: + print '\tDefault', self.default or 'None' + + def specify(self, k, v): + """Specify the option. Called when the option is set + from the command line. + + k option switch used + v optional value given (if any) + """ + if k in self.optkeys: + if self.val is None and v: + self.opts.err("Option '%s' does not take a value" % k) + self.specified_opt = k + self.specified_val = v + if self.fn: + self.fn(self, k, v) + return 1 + else: + return 0 + + def specified(self): + """Test whether the option has been specified: set + from the command line. + """ + return self.specified_opt + +class OptVals: + """Class to hold option values. + """ + pass + +class Opts: + """Container for options. + """ + def __init__(self, use=None): + """Options constructor. + + use usage string + """ + self.use = use + # List of options. + self.options = [] + # Options indexed by name. + self.options_map = {} + # Command-line arguments. + self.argv = [] + # Option values. + self.vals = OptVals() + self.vals.quiet = 0 + # Variables for default scripts. + self.vars = {} + + def __repr__(self): + return '\n'.join(map(str, self.options)) + + __str__ = __repr__ + + def opt(self, name, **args): + """Add an option. + + name option name + **args keyword params for option constructor + """ + x = Opt(self, name, **args) + self.options.append(x) + self.options_map[name] = x + return x + + def setvar(self, var, val): + """Set a default script variable. + """ + self.vars[var] = val + + def getvar(self, var): + """Get a default script variable. + """ + return self.vars.get(var) + + def option(self, name): + """Get an option (object). + """ + return self.options_map.get(name) + + def setopt(self, name, val): + """Set an option value. + An option can also be set using 'opts.vals.name = val'. + """ + setattr(self.vals, name, val) + + def getopt(self, name): + """Get an option value. + An option value can also be got using 'opts.vals.name'. + """ + getattr(self.vals, name) + + def specified(self, name): + """Test if an option has been specified. + """ + opt = self.option(name) + return opt and opt.specified() + + def err(self, msg): + """Print an error to stderr and exit. + """ + print >>sys.stderr, "Error:", msg + sys.exit(1) + + def info(self, msg): + """Print a message to stdout (unless quiet is set). + """ + if self.vals.quiet: return + print msg + + def warn(self, msg): + """Print a warning to stdout. + """ + print >>sys.stderr, "Warning:", msg + + def parse(self, argv): + """Parse arguments argv using the options. + + return remaining arguments + """ + self.argv = argv + (vals, args) = getopt(argv[1:], self.short_opts(), self.long_opts()) + self.args = args + for (k, v) in vals: + for opt in self.options: + if opt.specify(k, v): break + else: + print >>sys.stderr, "Error: Unknown option:", k + self.usage() + return args + + def short_opts(self): + """Get short options specifier for getopt. + """ + l = [] + for x in self.options: + y = x.short_opt() + if not y: continue + l.append(y) + return ''.join(l) + + def long_opts(self): + """Get long options specifier for getopt. + """ + l = [] + for x in self.options: + y = x.long_opt() + if not y: continue + l.append(y) + return l + + def usage(self): + print 'Usage: ', self.argv[0], self.use or 'OPTIONS' + for opt in self.options: + opt.show() + + def load_defaults(self): + """Load a defaults script. Assumes these options set: + 'path' search path + 'default' script name + """ + for x in [ '' ] + self.vals.path.split(':'): + if x: + p = os.path.join(x, self.vals.defaults) + else: + p = self.vals.defaults + if os.path.exists(p): + self.load(p) + break + else: + self.err("Cannot open defaults file %s" % self.vals.defaults) + + def load(self, defaults, help=0): + """Load a defaults file. Local variables in the file + are used to set options with the same names. + Variables are not used to set options that are already specified. + """ + # Create global and lobal dicts for the file. + # Initialize locals to the vars. + # Use exec to do the standard imports and + # define variables we are passing to the script. + globals = {} + locals = {} + locals.update(self.vars) + cmd = '\n'.join(["import sys", + "import os", + "import os.path", + "xm_file = '%s'" % defaults, + "xm_help = %d" % help ]) + exec cmd in globals, locals + execfile(defaults, globals, locals) + if help: return + # Extract the values set by the script and set the corresponding + # options, if not set on the command line. + vtypes = [ types.StringType, + types.ListType, + types.IntType, + types.FloatType + ] + for (k, v) in locals.items(): + if self.specified(k): continue + if not(type(v) in vtypes): continue + self.setopt(k, v) + +def set_true(opt, k, v): + """Set an option true.""" + opt.set(1) + +def set_false(opt, k, v): + """Set an option false.""" + opt.set(0) + +def set_value(opt, k, v): + """Set an option to a valoue.""" + opt.set(v) + +def set_int(opt, k, v): + """Set an option to an integer value.""" + try: + v = int(v) + except: + opt.opts.err('Invalid value: ' + str(v)) + opt.set(v) + +def append_value(opt, k, v): + """Append a value to a list option.""" + opt.append(v) + +def set_var(opt, k, v): + """Set a default script variable. + """ + (var, val) = v.strip().split('=') + opt.opts.setvar(var.strip(), val.strip()) + diff --git a/tools/xen/lib/xm/shutdown.py b/tools/xen/lib/xm/shutdown.py new file mode 100644 index 0000000000..7dc81008f1 --- /dev/null +++ b/tools/xen/lib/xm/shutdown.py @@ -0,0 +1,75 @@ +# Copyright (C) 2004 Mike Wray +"""Domain shutdown. +""" +import string +import sys +import time + +from xen.xend.XendClient import server +from xen.xend.xm.opts import * + +gopts = Opts(use="""[options] [DOM] + +Shutdown one or more domains gracefully.""") + +gopts.opt('help', short='h', + fn=set_true, default=0, + use="Print this help.") + +gopts.opt('all', short='a', + fn=set_true, default=0, + use="Shutdown all domains.") + +gopts.opt('wait', short='w', + fn=set_true, default=0, + use='Wait for shutdown to complete.') + +gopts.opt('norestart', short='n', + fn=set_true, default=0, + use='Prevent domain restart.') + +def shutdown(opts, doms, wait): + def domains(): + return [ int(a) for a in server.xend_domains() ] + if doms == None: doms = domains() + if 0 in doms: + doms.remove(0) + for d in doms: + server.xend_domain_shutdown(d) + if wait: + while doms: + alive = domains() + dead = [] + for d in doms: + if d in alive: continue + dead.append(d) + for d in dead: + opts.info("Domain %d terminated" % d) + doms.remove(d) + time.sleep(1) + opts.info("All domains terminated") + +def main_all(opts, args): + shutdown(opts, None, opts.vals.wait) + +def main_dom(opts, args): + if len(args) < 1: opts.err('Missing domain') + dom = args[0] + try: + domid = int(dom) + except: + opts.err('Invalid domain: ' + dom) + shutdown(opts, [ domid ], opts.vals.wait) + +def main(argv): + opts = gopts + args = opts.parse(argv) + if opts.vals.help: + opts.usage() + return + print 'shutdown.main>', len(args), args + if opts.vals.all: + main_all(opts, args) + else: + main_dom(opts, args) + diff --git a/tools/xen/netfix b/tools/xen/netfix new file mode 100644 index 0000000000..def4e28a6c --- /dev/null +++ b/tools/xen/netfix @@ -0,0 +1,61 @@ +#!/usr/bin/python +# -*- mode: python; -*- +# Copyright (C) 2004 Mike Wray +#============================================================================ +# Move the IP address from eth0 onto the Xen bridge (nbe-br). +# Only works if the bridge control utils (brctl) have been installed. +#============================================================================ + +from getopt import getopt +from xen.xend.XendBridge import * + +short_options = 'hvqni:b:c' +long_options = ['help', 'verbose', 'quiet', + 'interface=', 'bridge=', 'create'] + +def usage(): + print """Usage: + %s [options] + + Reconfigure routing so that has the IP address from + . This lets IP carry on working when + is attached to for virtual networking. + Uses brctl to add to , + so this can be run before any domains have been created. + """ % sys.argv[0] + print """ + -i, --interface interface, default %(interface)s. + -b, --bridge bridge, default %(bridge)s. + -c, --create create the bridge. + -v, --verbose Print commands. + -q, --quiet Don't print commands. + -n, --dry-run Don't execute commands. + -h, --help Print this help. + """ % defaults + sys.exit(1) + + +def main(): + lopts = set_opts(Opts(defaults)) + lopts.dryrun = 0 + (options, args) = getopt(sys.argv[1:], short_options, long_options) + if args: usage() + for k, v in options: + if k in ['-h', '--help']: + usage() + elif k in ['-c', '--create']: + lopts.create = 1 + elif k in ['-i', '--interface']: + lopts.interface = v + elif k in ['-b', '--bridge']: + lopts.bridge = v + elif k in ['-q', '--quiet']: + lopts.verbose = 0 + elif k in ['-v', '--verbose']: + lopts.verbose = 1 + elif k in ['-n', '--dry-run']: + lopts.dryrun = 1 + reconfigure(lopts.interface, lopts.bridge) + +if __name__ == '__main__': + main() diff --git a/tools/xen/setup.py b/tools/xen/setup.py new file mode 100644 index 0000000000..e099e00166 --- /dev/null +++ b/tools/xen/setup.py @@ -0,0 +1,16 @@ + +from distutils.core import setup, Extension + +setup(name = 'xen', + version = '1.0', + description = 'Xen', + author = 'Mike Wray', + author_email = 'mike.wray@hp.com', + packages = ['xen', + 'xen.ext', + 'xen.xend', + 'xen.xend.server', + 'xen.xm', + ], + package_dir = { 'xen': 'lib' }, + ) diff --git a/tools/xen/xend b/tools/xen/xend new file mode 100644 index 0000000000..65cfe8d820 --- /dev/null +++ b/tools/xen/xend @@ -0,0 +1,41 @@ +#!/usr/bin/python +# -*- mode: python; -*- +# Copyright (C) 2004 Mike Wray + +"""Xen management daemon. Lives in /usr/sbin. + Provides console server and HTTP management api. + + Run: + + xend start + + The daemon is stopped with: + + xend stop + + Unfortunately restarting it upsets the channel to dom0 and + domain management stops working - needs a reboot to fix. +""" +import os +import sys +from xen.xend.server import SrvDaemon + +def main(): + daemon = SrvDaemon.instance() + if not sys.argv[1:]: + print 'usage: %s {start|stop|restart}' % sys.argv[0] + elif os.fork(): + pid, status = os.wait() + return status >> 8 + elif sys.argv[1] == 'start': + return daemon.start() + elif sys.argv[1] == 'stop': + return daemon.stop() + elif sys.argv[1] == 'restart': + return daemon.stop() or daemon.start() + else: + print 'not an option:', sys.argv[1] + return 1 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/xen/xm b/tools/xen/xm new file mode 100755 index 0000000000..09d8036f0f --- /dev/null +++ b/tools/xen/xm @@ -0,0 +1,6 @@ +#!/usr/bin/python +# -*- mode: python; -*- +import sys +from xen.xm import main + +main.main(sys.argv) diff --git a/tools/xend-old/Makefile b/tools/xend-old/Makefile new file mode 100644 index 0000000000..8e28c0386a --- /dev/null +++ b/tools/xend-old/Makefile @@ -0,0 +1,17 @@ + +all: + python setup.py build + +install: all + if [ "$(prefix)" = "" ]; then \ + python setup.py install; \ + elif [ "$(dist)" = "yes" ]; then \ + python setup.py install --home="$(prefix)"; \ + else \ + python setup.py install --root="$(prefix)"; \ + fi + mkdir -p $(prefix)/usr/sbin + install -m0755 xend $(prefix)/usr/sbin + +clean: + rm -rf build *.pyc *.pyo *.a *.so *.o *~ *.rpm diff --git a/tools/xend-old/lib/__init__.py b/tools/xend-old/lib/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/xend-old/lib/blkif.py b/tools/xend-old/lib/blkif.py new file mode 100644 index 0000000000..51431b694a --- /dev/null +++ b/tools/xend-old/lib/blkif.py @@ -0,0 +1,214 @@ + +################################################################# +## xend/blkif.py -- Block-interface management functions for Xend +## Copyright (c) 2004, K A Fraser (University of Cambridge) +################################################################# + +import errno, re, os, select, signal, socket, sys +import xend.main, xend.console, xend.manager, xend.utils, Xc + +CMSG_BLKIF_BE = 1 +CMSG_BLKIF_FE = 2 +CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED = 0 +CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED = 32 +CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED = 32 +CMSG_BLKIF_FE_INTERFACE_CONNECT = 33 +CMSG_BLKIF_FE_INTERFACE_DISCONNECT = 34 +CMSG_BLKIF_BE_CREATE = 0 +CMSG_BLKIF_BE_DESTROY = 1 +CMSG_BLKIF_BE_CONNECT = 2 +CMSG_BLKIF_BE_DISCONNECT = 3 +CMSG_BLKIF_BE_VBD_CREATE = 4 +CMSG_BLKIF_BE_VBD_DESTROY = 5 +CMSG_BLKIF_BE_VBD_GROW = 6 +CMSG_BLKIF_BE_VBD_SHRINK = 7 + +BLKIF_DRIVER_STATUS_DOWN = 0 +BLKIF_DRIVER_STATUS_UP = 1 + +pendmsg = None +pendaddr = None + +recovery = False # Is a recovery in progress? (if so we'll need to notify guests) +be_port = None # Port object for backend domain + +def backend_tx_req(msg): + port = xend.blkif.be_port + if not port: + print "BUG: attempt to transmit request to non-existant blkif driver" + if port.space_to_write_request(): + port.write_request(msg) + port.notify() + else: + xend.blkif.pendmsg = msg + +def backend_rx_req(port, msg): + port.write_response(msg) + subtype = (msg.get_header())['subtype'] + print "Received blkif-be request, subtype %d" % subtype + if subtype == CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: + status = (msg.get_payload())['status'] + if status == BLKIF_DRIVER_STATUS_UP: + if xend.blkif.recovery: + # Nasty hack: we count the number of VBDs we reattach so that + # we'll know when to notify the guests. Must make this better! + interface.rebuilt_so_far = 0 + interface.nr_to_rebuild = 0 + print "New blkif backend now UP, rebuilding VBDs:" + for blkif_key in interface.list.keys(): + blkif = interface.list[blkif_key] + blkif.create() + for vdev in blkif.devices.keys(): + blkif.reattach_device(vdev) + interface.nr_to_rebuild += 1 + else: + print "Unexpected block backend driver status: %d" % status + + +def backend_rx_rsp(port, msg): + subtype = (msg.get_header())['subtype'] + print "Received blkif-be response, subtype %d" % subtype + if subtype == CMSG_BLKIF_BE_CREATE: + rsp = { 'success': True } + xend.main.send_management_response(rsp, xend.blkif.pendaddr) + elif subtype == CMSG_BLKIF_BE_CONNECT: + pl = msg.get_payload() + (dom, hnd, frame, evtchn, st) = (pl['domid'], pl['blkif_handle'], + pl['shmem_frame'], pl['evtchn'], + pl['status']) + blkif = interface.list[xend.main.port_from_dom(dom).local_port] + msg = xend.utils.message(CMSG_BLKIF_FE, + CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED, 0, + { 'handle' : 0, 'status' : 2, + 'evtchn' : blkif.evtchn['port2'] }) + blkif.ctrlif_tx_req(xend.main.port_list[blkif.key], msg) + elif subtype == CMSG_BLKIF_BE_VBD_CREATE: + pl = msg.get_payload() + (dom, hnd, vdev, ro, st) = (pl['domid'], pl['blkif_handle'], + pl['vdevice'], pl['readonly'], + pl['status']) + blkif = interface.list[xend.main.port_from_dom(dom).local_port] + (pdev, start_sect, nr_sect, readonly) = blkif.devices[vdev] + msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW, 0, + { 'domid' : dom, 'blkif_handle' : 0, + 'vdevice' : vdev, + 'extent.sector_start' : start_sect, + 'extent.sector_length' : nr_sect, + 'extent.device' : pdev }) + backend_tx_req(msg) + elif subtype == CMSG_BLKIF_BE_VBD_GROW: + if not xend.blkif.recovery: + rsp = { 'success': True } + xend.main.send_management_response(rsp, xend.blkif.pendaddr) + else: + interface.rebuilt_so_far += 1 + if interface.rebuilt_so_far == interface.nr_to_rebuild: + print "Rebuilt VBDs, notifying guests:" + for blkif_key in interface.list.keys(): + blkif = interface.list[blkif_key] + print " Notifying %d" % blkif.dom + msg = xend.utils.message(CMSG_BLKIF_FE, + CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED, + 0, { 'handle' : 0, 'status' : 1 }) + blkif.ctrlif_tx_req(xend.main.port_from_dom(blkif.dom),msg) + xend.blkif.recovery = False + print "Done notifying guests" + + +def backend_do_work(port): + global pendmsg + if pendmsg and port.space_to_write_request(): + port.write_request(pendmsg) + pendmsg = None + return True + return False + + +class interface: + + # Dictionary of all block-device interfaces. + list = {} + + # NB. 'key' is an opaque value that has no meaning in this class. + def __init__(self, dom, key): + self.dom = dom + self.key = key + self.devices = {} + self.pendmsg = None + interface.list[key] = self + self.create() + + def create(self): + msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE, 0, + { 'domid' : self.dom, 'blkif_handle' : 0 }) + xend.blkif.pendaddr = xend.main.mgmt_req_addr + backend_tx_req(msg) + + # Attach a device to the specified interface + def attach_device(self, vdev, pdev, start_sect, nr_sect, readonly): + if self.devices.has_key(vdev): + return False + self.devices[vdev] = (pdev, start_sect, nr_sect, readonly) + msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE, 0, + { 'domid' : self.dom, 'blkif_handle' : 0, + 'vdevice' : vdev, 'readonly' : readonly }) + xend.blkif.pendaddr = xend.main.mgmt_req_addr + backend_tx_req(msg) + return True + + def reattach_device(self, vdev): + (pdev, start_sect, nr_sect, readonly) = self.devices[vdev] + msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE, + 0, { 'domid' : self.dom, + 'blkif_handle' : 0, + 'vdevice' : vdev, + 'readonly' : readonly }) + xend.blkif.pendaddr = xend.main.mgmt_req_addr + backend_tx_req(msg) + + # Completely destroy this interface. + def destroy(self): + del interface.list[self.key] + msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY, 0, + { 'domid' : self.dom, 'blkif_handle' : 0 }) + backend_tx_req(msg) + + + # The parameter @port is the control-interface event channel. This method + # returns True if messages were written to the control interface. + def ctrlif_transmit_work(self, port): + if self.pendmsg and port.space_to_write_request(): + port.write_request(self.pendmsg) + self.pendmsg = None + return True + return False + + def ctrlif_tx_req(self, port, msg): + if port.space_to_write_request(): + port.write_request(msg) + port.notify() + else: + self.pendmsg = msg + + def ctrlif_rx_req(self, port, msg): + port.write_response(msg) + subtype = (msg.get_header())['subtype'] + if subtype == CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED: + msg = xend.utils.message(CMSG_BLKIF_FE, + CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED, + 0, + { 'handle' : 0, 'status' : 1 }) + self.ctrlif_tx_req(port, msg) + elif subtype == CMSG_BLKIF_FE_INTERFACE_CONNECT: + pl = msg.get_payload() + (hnd, frame) = (pl['handle'], pl['shmem_frame']) + xc = Xc.new() + self.evtchn = xc.evtchn_bind_interdomain( + dom1=xend.blkif.be_port.remote_dom, + dom2=self.dom) + msg = xend.utils.message(CMSG_BLKIF_BE, + CMSG_BLKIF_BE_CONNECT, 0, + { 'domid' : self.dom, 'blkif_handle' : 0, + 'shmem_frame' : frame, + 'evtchn' : self.evtchn['port1'] }) + backend_tx_req(msg) diff --git a/tools/xend-old/lib/console.py b/tools/xend-old/lib/console.py new file mode 100644 index 0000000000..57898817f5 --- /dev/null +++ b/tools/xend-old/lib/console.py @@ -0,0 +1,180 @@ + +############################################################# +## xend/console.py -- Console-management functions for Xend +## Copyright (c) 2004, K A Fraser (University of Cambridge) +############################################################# + +import errno, re, os, select, signal, socket, struct, sys +import xend.blkif, xend.main, xend.manager, xend.utils, Xc + +## +## interface: +## Each control interface owns an instance of this class, which manages +## the current state of the console interface. Normally a console interface +## will be one of two state: +## LISTENING: listening for a connection on TCP port 'self.port' +## CONNECTED: sending/receiving console data on TCP port 'self.port' +## +## A dictionary of all active interfaces, indexed by TCP socket descriptor, +## is accessible as 'interface.list_by_fd'. +## +## NB. When a class instance is to be destroyed you *must* call the 'close' +## method. Otherwise a stale reference will eb left in the interface list. +## +class interface: + + # The various states that a console interface may be in. + CLOSED = 0 # No console activity + LISTENING = 1 # Listening on port 'self.port'. Socket object 'self.sock'. + CONNECTED = 2 # Active connection on 'self.port'. Socket obj 'self.sock'. + + + # Dictionary of all active (non-closed) console interfaces. + list_by_fd = {} + + + # Dictionary of all console interfaces, closed and open. + list = {} + + + # NB. 'key' is an opaque value that has no meaning in this class. + def __init__(self, port, key): + self.status = interface.CLOSED + self.port = port + self.key = key + self.rbuf = xend.utils.buffer() + self.wbuf = xend.utils.buffer() + interface.list[key] = self + + + # Is this interface closed (inactive)? + def closed(self): + return self.status == interface.CLOSED + + + # Is this interface listening? + def listening(self): + return self.status == interface.LISTENING + + + # Is this interface active and connected? + def connected(self): + return self.status == interface.CONNECTED + + + # Close the interface, if it is not closed already. + def close(self): + if not self.closed(): + del interface.list_by_fd[self.sock.fileno()] + self.sock.close() + del self.sock + self.status = interface.CLOSED + + + # Move the interface into the 'listening' state. Opens a new listening + # socket and updates 'list_by_fd'. + def listen(self): + # Close old socket (if any), and create a fresh one. + self.close() + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + + try: + # Turn the new socket into a non-blocking listener. + self.sock.setblocking(False) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.sock.bind(('', self.port)) + self.sock.listen(1) + + # Announce the new status of thsi interface. + self.status = interface.LISTENING + interface.list_by_fd[self.sock.fileno()] = self + + except: + # In case of trouble ensure we get rid of dangling socket reference + self.sock.close() + del self.sock + raise + + + # Move a listening interface into the 'connected' state. + def connect(self): + # Pick up a new connection, if one is available. + try: + (sock, addr) = self.sock.accept() + except: + return 0 + sock.setblocking(False) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + # Close the listening socket. + self.sock.close() + + # Publish the new socket and the new interface state. + self.sock = sock + self.status = interface.CONNECTED + interface.list_by_fd[self.sock.fileno()] = self + return 1 + + + # Completely sestroy a console interface. + def destroy(self): + self.close() + del interface.list[self.key] + + + # Do work triggered by resource availability on a console-interface socket. + def socket_work(self): + # If the interface is listening, check for pending connections. + if self.listening(): + self.connect() + + # All done if the interface is not connected. + if not self.connected(): + return + + # Send as much pending data as possible via the socket. + while not self.rbuf.empty(): + try: + bytes = self.sock.send(self.rbuf.peek()) + if bytes > 0: + self.rbuf.discard(bytes) + except socket.error, error: + pass + + # Read as much data as is available. Don't worry about + # overflowing our buffer: it's more important to read the + # incoming data stream and detect errors or closure of the + # remote end in a timely manner. + try: + while 1: + data = self.sock.recv(2048) + # Return of zero means the remote end has disconnected. + # We therefore return the console interface to listening. + if not data: + self.listen() + break + self.wbuf.write(data) + except socket.error, error: + # Assume that most errors mean that the connection is dead. + # In such cases we return the interface to 'listening' state. + if error[0] != errno.EAGAIN: + print "Better return to listening" + self.listen() + print "New status: " + str(self.status) + + + # The parameter @port is the control-interface event channel. This method + # returns True if messages were written to the control interface. + def ctrlif_transmit_work(self, port): + work_done = False + while not self.wbuf.empty() and port.space_to_write_request(): + msg = xend.utils.message(0, 0, 0) + msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD)) + port.write_request(msg) + work_done = True + return work_done + + + def ctrlif_rx_req(self, port, msg): + self.rbuf.write(msg.get_payload()) + port.write_response(msg) diff --git a/tools/xend-old/lib/domain_controller.h b/tools/xend-old/lib/domain_controller.h new file mode 100644 index 0000000000..76dd164fcb --- /dev/null +++ b/tools/xend-old/lib/domain_controller.h @@ -0,0 +1,532 @@ +/****************************************************************************** + * domain_controller.h + * + * Interface to server controller (e.g., 'xend'). This header file defines the + * interface that is shared with guest OSes. + * + * Copyright (c) 2004, K A Fraser + */ + +#ifndef __DOMAIN_CONTROLLER_H__ +#define __DOMAIN_CONTROLLER_H__ + + +#ifndef BASIC_START_INFO +#error "Xen header file hypervisor-if.h must already be included here." +#endif + + +/* + * EXTENDED BOOTSTRAP STRUCTURE FOR NEW DOMAINS. + */ + +typedef struct { + BASIC_START_INFO; + u16 domain_controller_evtchn; /* 320 */ +} PACKED extended_start_info_t; /* 322 bytes */ +#define SIF_BLK_BE_DOMAIN (1<<4) /* Is this a block backend domain? */ +#define SIF_NET_BE_DOMAIN (1<<5) /* Is this a net backend domain? */ + + +/* + * Reason codes for SCHEDOP_shutdown. These are opaque to Xen but may be + * interpreted by control software to determine the appropriate action. These + * are only really advisories: the controller can actually do as it likes. + */ +#define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */ +#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */ +#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ + + +/* + * CONTROLLER MESSAGING INTERFACE. + */ + +typedef struct { + u8 type; /* 0: echoed in response */ + u8 subtype; /* 1: echoed in response */ + u8 id; /* 2: echoed in response */ + u8 length; /* 3: number of bytes in 'msg' */ + u8 msg[60]; /* 4: type-specific message data */ +} PACKED control_msg_t; /* 64 bytes */ + +#define CONTROL_RING_SIZE 8 +typedef u32 CONTROL_RING_IDX; +#define MASK_CONTROL_IDX(_i) ((_i)&(CONTROL_RING_SIZE-1)) + +typedef struct { + control_msg_t tx_ring[CONTROL_RING_SIZE]; /* 0: guest -> controller */ + control_msg_t rx_ring[CONTROL_RING_SIZE]; /* 512: controller -> guest */ + CONTROL_RING_IDX tx_req_prod, tx_resp_prod; /* 1024, 1028 */ + CONTROL_RING_IDX rx_req_prod, rx_resp_prod; /* 1032, 1036 */ +} PACKED control_if_t; /* 1040 bytes */ + +/* + * Top-level command types. + */ +#define CMSG_CONSOLE 0 /* Console */ +#define CMSG_BLKIF_BE 1 /* Block-device backend */ +#define CMSG_BLKIF_FE 2 /* Block-device frontend */ +#define CMSG_NETIF_BE 3 /* Network-device backend */ +#define CMSG_NETIF_FE 4 /* Network-device frontend */ +#define CMSG_SHUTDOWN 6 /* Shutdown messages */ + + +/****************************************************************************** + * CONSOLE DEFINITIONS + */ + +/* + * Subtypes for console messages. + */ +#define CMSG_CONSOLE_DATA 0 + + +/****************************************************************************** + * BLOCK-INTERFACE FRONTEND DEFINITIONS + */ + +/* Messages from domain controller to guest. */ +#define CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED 0 + +/* Messages from guest to domain controller. */ +#define CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED 32 +#define CMSG_BLKIF_FE_INTERFACE_CONNECT 33 +#define CMSG_BLKIF_FE_INTERFACE_DISCONNECT 34 + +/* These are used by both front-end and back-end drivers. */ +#define blkif_vdev_t u16 +#define blkif_pdev_t u16 +#define blkif_sector_t u64 + +/* + * CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED: + * Notify a guest about a status change on one of its block interfaces. + * If the interface is DESTROYED or DOWN then the interface is disconnected: + * 1. The shared-memory frame is available for reuse. + * 2. Any unacknowledged messgaes pending on the interface were dropped. + */ +#define BLKIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */ +#define BLKIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */ +#define BLKIF_INTERFACE_STATUS_CONNECTED 2 /* Exists and is connected. */ +typedef struct { + u32 handle; /* 0 */ + u32 status; /* 4 */ + u16 evtchn; /* 8: (only if status == BLKIF_INTERFACE_STATUS_CONNECTED). */ +} PACKED blkif_fe_interface_status_changed_t; /* 10 bytes */ + +/* + * CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED: + * Notify the domain controller that the front-end driver is DOWN or UP. + * When the driver goes DOWN then the controller will send no more + * status-change notifications. When the driver comes UP then the controller + * will send a notification for each interface that currently exists. + * If the driver goes DOWN while interfaces are still UP, the domain + * will automatically take the interfaces DOWN. + */ +#define BLKIF_DRIVER_STATUS_DOWN 0 +#define BLKIF_DRIVER_STATUS_UP 1 +typedef struct { + /* IN */ + u32 status; /* 0: BLKIF_DRIVER_STATUS_??? */ + /* OUT */ + /* + * Tells driver how many interfaces it should expect to immediately + * receive notifications about. + */ + u32 nr_interfaces; /* 4 */ +} PACKED blkif_fe_driver_status_changed_t; /* 8 bytes */ + +/* + * CMSG_BLKIF_FE_INTERFACE_CONNECT: + * If successful, the domain controller will acknowledge with a + * STATUS_CONNECTED message. + */ +typedef struct { + u32 handle; /* 0 */ + u32 __pad; + memory_t shmem_frame; /* 8 */ + MEMORY_PADDING; +} PACKED blkif_fe_interface_connect_t; /* 16 bytes */ + +/* + * CMSG_BLKIF_FE_INTERFACE_DISCONNECT: + * If successful, the domain controller will acknowledge with a + * STATUS_DISCONNECTED message. + */ +typedef struct { + u32 handle; /* 0 */ +} PACKED blkif_fe_interface_disconnect_t; /* 4 bytes */ + + +/****************************************************************************** + * BLOCK-INTERFACE BACKEND DEFINITIONS + */ + +/* Messages from domain controller. */ +#define CMSG_BLKIF_BE_CREATE 0 /* Create a new block-device interface. */ +#define CMSG_BLKIF_BE_DESTROY 1 /* Destroy a block-device interface. */ +#define CMSG_BLKIF_BE_CONNECT 2 /* Connect i/f to remote driver. */ +#define CMSG_BLKIF_BE_DISCONNECT 3 /* Disconnect i/f from remote driver. */ +#define CMSG_BLKIF_BE_VBD_CREATE 4 /* Create a new VBD for an interface. */ +#define CMSG_BLKIF_BE_VBD_DESTROY 5 /* Delete a VBD from an interface. */ +#define CMSG_BLKIF_BE_VBD_GROW 6 /* Append an extent to a given VBD. */ +#define CMSG_BLKIF_BE_VBD_SHRINK 7 /* Remove last extent from a given VBD. */ + +/* Messages to domain controller. */ +#define CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED 32 + +/* + * Message request/response definitions for block-device messages. + */ + +typedef struct { + blkif_sector_t sector_start; /* 0 */ + blkif_sector_t sector_length; /* 8 */ + blkif_pdev_t device; /* 16 */ + u16 __pad; /* 18 */ +} PACKED blkif_extent_t; /* 20 bytes */ + +/* Non-specific 'okay' return. */ +#define BLKIF_BE_STATUS_OKAY 0 +/* Non-specific 'error' return. */ +#define BLKIF_BE_STATUS_ERROR 1 +/* The following are specific error returns. */ +#define BLKIF_BE_STATUS_INTERFACE_EXISTS 2 +#define BLKIF_BE_STATUS_INTERFACE_NOT_FOUND 3 +#define BLKIF_BE_STATUS_INTERFACE_CONNECTED 4 +#define BLKIF_BE_STATUS_VBD_EXISTS 5 +#define BLKIF_BE_STATUS_VBD_NOT_FOUND 6 +#define BLKIF_BE_STATUS_OUT_OF_MEMORY 7 +#define BLKIF_BE_STATUS_EXTENT_NOT_FOUND 8 +#define BLKIF_BE_STATUS_MAPPING_ERROR 9 + +/* This macro can be used to create an array of descriptive error strings. */ +#define BLKIF_BE_STATUS_ERRORS { \ + "Okay", \ + "Non-specific error", \ + "Interface already exists", \ + "Interface not found", \ + "Interface is still connected", \ + "VBD already exists", \ + "VBD not found", \ + "Out of memory", \ + "Extent not found for VBD", \ + "Could not map domain memory" } + +/* + * CMSG_BLKIF_BE_CREATE: + * When the driver sends a successful response then the interface is fully + * created. The controller will send a DOWN notification to the front-end + * driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 blkif_handle; /* 4: Domain-specific interface handle. */ + /* OUT */ + u32 status; /* 8 */ +} PACKED blkif_be_create_t; /* 12 bytes */ + +/* + * CMSG_BLKIF_BE_DESTROY: + * When the driver sends a successful response then the interface is fully + * torn down. The controller will send a DESTROYED notification to the + * front-end driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify interface to be destroyed. */ + u32 blkif_handle; /* 4: ...ditto... */ + /* OUT */ + u32 status; /* 8 */ +} PACKED blkif_be_destroy_t; /* 12 bytes */ + +/* + * CMSG_BLKIF_BE_CONNECT: + * When the driver sends a successful response then the interface is fully + * connected. The controller will send a CONNECTED notification to the + * front-end driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 blkif_handle; /* 4: Domain-specific interface handle. */ + memory_t shmem_frame; /* 8: Page cont. shared comms window. */ + MEMORY_PADDING; + u32 evtchn; /* 16: Event channel for notifications. */ + /* OUT */ + u32 status; /* 20 */ +} PACKED blkif_be_connect_t; /* 24 bytes */ + +/* + * CMSG_BLKIF_BE_DISCONNECT: + * When the driver sends a successful response then the interface is fully + * disconnected. The controller will send a DOWN notification to the front-end + * driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 blkif_handle; /* 4: Domain-specific interface handle. */ + /* OUT */ + u32 status; /* 8 */ +} PACKED blkif_be_disconnect_t; /* 12 bytes */ + +/* CMSG_BLKIF_BE_VBD_CREATE */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify blkdev interface. */ + u32 blkif_handle; /* 4: ...ditto... */ + blkif_vdev_t vdevice; /* 8: Interface-specific id for this VBD. */ + u16 readonly; /* 10: Non-zero -> VBD isn't writeable. */ + /* OUT */ + u32 status; /* 12 */ +} PACKED blkif_be_vbd_create_t; /* 16 bytes */ + +/* CMSG_BLKIF_BE_VBD_DESTROY */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify blkdev interface. */ + u32 blkif_handle; /* 4: ...ditto... */ + blkif_vdev_t vdevice; /* 8: Interface-specific id of the VBD. */ + u16 __pad; /* 10 */ + /* OUT */ + u32 status; /* 12 */ +} PACKED blkif_be_vbd_destroy_t; /* 16 bytes */ + +/* CMSG_BLKIF_BE_VBD_GROW */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify blkdev interface. */ + u32 blkif_handle; /* 4: ...ditto... */ + blkif_extent_t extent; /* 8: Physical extent to append to VBD. */ + blkif_vdev_t vdevice; /* 28: Interface-specific id of the VBD. */ + u16 __pad; /* 30 */ + /* OUT */ + u32 status; /* 32 */ +} PACKED blkif_be_vbd_grow_t; /* 36 bytes */ + +/* CMSG_BLKIF_BE_VBD_SHRINK */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify blkdev interface. */ + u32 blkif_handle; /* 4: ...ditto... */ + blkif_vdev_t vdevice; /* 8: Interface-specific id of the VBD. */ + u16 __pad; /* 10 */ + /* OUT */ + u32 status; /* 12 */ +} PACKED blkif_be_vbd_shrink_t; /* 16 bytes */ + +/* + * CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: + * Notify the domain controller that the back-end driver is DOWN or UP. + * If the driver goes DOWN while interfaces are still UP, the controller + * will automatically send DOWN notifications. + */ +typedef struct { + u32 status; /* 0: BLKIF_DRIVER_STATUS_??? */ +} PACKED blkif_be_driver_status_changed_t; /* 4 bytes */ + + +/****************************************************************************** + * NETWORK-INTERFACE FRONTEND DEFINITIONS + */ + +/* Messages from domain controller to guest. */ +#define CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED 0 + +/* Messages from guest to domain controller. */ +#define CMSG_NETIF_FE_DRIVER_STATUS_CHANGED 32 +#define CMSG_NETIF_FE_INTERFACE_CONNECT 33 +#define CMSG_NETIF_FE_INTERFACE_DISCONNECT 34 + +/* + * CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED: + * Notify a guest about a status change on one of its network interfaces. + * If the interface is DESTROYED or DOWN then the interface is disconnected: + * 1. The shared-memory frame is available for reuse. + * 2. Any unacknowledged messgaes pending on the interface were dropped. + */ +#define NETIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */ +#define NETIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */ +#define NETIF_INTERFACE_STATUS_CONNECTED 2 /* Exists and is connected. */ +typedef struct { + u32 handle; /* 0 */ + u32 status; /* 4 */ + u16 evtchn; /* 8: status == NETIF_INTERFACE_STATUS_CONNECTED */ + u8 mac[6]; /* 10: status == NETIF_INTERFACE_STATUS_CONNECTED */ +} PACKED netif_fe_interface_status_changed_t; /* 16 bytes */ + +/* + * CMSG_NETIF_FE_DRIVER_STATUS_CHANGED: + * Notify the domain controller that the front-end driver is DOWN or UP. + * When the driver goes DOWN then the controller will send no more + * status-change notifications. When the driver comes UP then the controller + * will send a notification for each interface that currently exists. + * If the driver goes DOWN while interfaces are still UP, the domain + * will automatically take the interfaces DOWN. + */ +#define NETIF_DRIVER_STATUS_DOWN 0 +#define NETIF_DRIVER_STATUS_UP 1 +typedef struct { + /* IN */ + u32 status; /* 0: NETIF_DRIVER_STATUS_??? */ + /* OUT */ + /* + * Tells driver how many interfaces it should expect to immediately + * receive notifications about. + */ + u32 nr_interfaces; /* 4 */ +} PACKED netif_fe_driver_status_changed_t; /* 8 bytes */ + +/* + * CMSG_NETIF_FE_INTERFACE_CONNECT: + * If successful, the domain controller will acknowledge with a + * STATUS_CONNECTED message. + */ +typedef struct { + u32 handle; /* 0 */ + u32 __pad; /* 4 */ + memory_t tx_shmem_frame; /* 8 */ + MEMORY_PADDING; + memory_t rx_shmem_frame; /* 16 */ + MEMORY_PADDING; +} PACKED netif_fe_interface_connect_t; /* 24 bytes */ + +/* + * CMSG_NETIF_FE_INTERFACE_DISCONNECT: + * If successful, the domain controller will acknowledge with a + * STATUS_DISCONNECTED message. + */ +typedef struct { + u32 handle; /* 0 */ +} PACKED netif_fe_interface_disconnect_t; /* 4 bytes */ + + +/****************************************************************************** + * NETWORK-INTERFACE BACKEND DEFINITIONS + */ + +/* Messages from domain controller. */ +#define CMSG_NETIF_BE_CREATE 0 /* Create a new net-device interface. */ +#define CMSG_NETIF_BE_DESTROY 1 /* Destroy a net-device interface. */ +#define CMSG_NETIF_BE_CONNECT 2 /* Connect i/f to remote driver. */ +#define CMSG_NETIF_BE_DISCONNECT 3 /* Disconnect i/f from remote driver. */ + +/* Messages to domain controller. */ +#define CMSG_NETIF_BE_DRIVER_STATUS_CHANGED 32 + +/* + * Message request/response definitions for net-device messages. + */ + +/* Non-specific 'okay' return. */ +#define NETIF_BE_STATUS_OKAY 0 +/* Non-specific 'error' return. */ +#define NETIF_BE_STATUS_ERROR 1 +/* The following are specific error returns. */ +#define NETIF_BE_STATUS_INTERFACE_EXISTS 2 +#define NETIF_BE_STATUS_INTERFACE_NOT_FOUND 3 +#define NETIF_BE_STATUS_INTERFACE_CONNECTED 4 +#define NETIF_BE_STATUS_OUT_OF_MEMORY 5 +#define NETIF_BE_STATUS_MAPPING_ERROR 6 + +/* This macro can be used to create an array of descriptive error strings. */ +#define NETIF_BE_STATUS_ERRORS { \ + "Okay", \ + "Non-specific error", \ + "Interface already exists", \ + "Interface not found", \ + "Interface is still connected", \ + "Out of memory", \ + "Could not map domain memory" } + +/* + * CMSG_NETIF_BE_CREATE: + * When the driver sends a successful response then the interface is fully + * created. The controller will send a DOWN notification to the front-end + * driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 netif_handle; /* 4: Domain-specific interface handle. */ + u8 mac[6]; /* 8 */ + u16 __pad; /* 14 */ + /* OUT */ + u32 status; /* 16 */ +} PACKED netif_be_create_t; /* 20 bytes */ + +/* + * CMSG_NETIF_BE_DESTROY: + * When the driver sends a successful response then the interface is fully + * torn down. The controller will send a DESTROYED notification to the + * front-end driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify interface to be destroyed. */ + u32 netif_handle; /* 4: ...ditto... */ + /* OUT */ + u32 status; /* 8 */ +} PACKED netif_be_destroy_t; /* 12 bytes */ + +/* + * CMSG_NETIF_BE_CONNECT: + * When the driver sends a successful response then the interface is fully + * connected. The controller will send a CONNECTED notification to the + * front-end driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 netif_handle; /* 4: Domain-specific interface handle. */ + memory_t tx_shmem_frame; /* 8: Page cont. tx shared comms window. */ + MEMORY_PADDING; + memory_t rx_shmem_frame; /* 16: Page cont. rx shared comms window. */ + MEMORY_PADDING; + u16 evtchn; /* 24: Event channel for notifications. */ + u16 __pad; /* 26 */ + /* OUT */ + u32 status; /* 28 */ +} PACKED netif_be_connect_t; /* 32 bytes */ + +/* + * CMSG_NETIF_BE_DISCONNECT: + * When the driver sends a successful response then the interface is fully + * disconnected. The controller will send a DOWN notification to the front-end + * driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 netif_handle; /* 4: Domain-specific interface handle. */ + /* OUT */ + u32 status; /* 8 */ +} PACKED netif_be_disconnect_t; /* 12 bytes */ + +/* + * CMSG_NETIF_BE_DRIVER_STATUS_CHANGED: + * Notify the domain controller that the back-end driver is DOWN or UP. + * If the driver goes DOWN while interfaces are still UP, the domain + * will automatically send DOWN notifications. + */ +typedef struct { + u32 status; /* 0: NETIF_DRIVER_STATUS_??? */ +} PACKED netif_be_driver_status_changed_t; /* 4 bytes */ + + +/****************************************************************************** + * SHUTDOWN DEFINITIONS + */ + +/* + * Subtypes for shutdown messages. + */ +#define CMSG_SHUTDOWN_POWEROFF 0 /* Clean shutdown (SHUTDOWN_poweroff). */ +#define CMSG_SHUTDOWN_REBOOT 1 /* Clean shutdown (SHUTDOWN_reboot). */ +#define CMSG_SHUTDOWN_SUSPEND 2 /* Create suspend info, then */ + /* SHUTDOWN_suspend. */ + +#endif /* __DOMAIN_CONTROLLER_H__ */ diff --git a/tools/xend-old/lib/main.py b/tools/xend-old/lib/main.py new file mode 100755 index 0000000000..0eecd17a05 --- /dev/null +++ b/tools/xend-old/lib/main.py @@ -0,0 +1,317 @@ + +########################################################### +## xend.py -- Xen controller daemon +## Copyright (c) 2004, K A Fraser (University of Cambridge) +########################################################### + +import errno, re, os, pwd, select, signal, socket, struct, sys, time +import xend.blkif, xend.netif, xend.console, xend.manager, xend.utils, Xc + + +# The following parameters could be placed in a configuration file. +PID = '/var/run/xend.pid' +LOG = '/var/log/xend.log' +USER = 'root' +CONTROL_DIR = '/var/run/xend' +UNIX_SOCK = 'management_sock' # relative to CONTROL_DIR + + +CMSG_CONSOLE = 0 +CMSG_BLKIF_BE = 1 +CMSG_BLKIF_FE = 2 +CMSG_NETIF_BE = 3 +CMSG_NETIF_FE = 4 + + +def port_from_dom(dom): + global port_list + for idx, port in port_list.items(): + if port.remote_dom == dom: + return port + return None + + +def send_management_response(response, addr): + try: + response = str(response) + print "Mgmt_rsp[%s]: %s" % (addr, response) + management_interface.sendto(response, addr) + except socket.error, error: + pass + + +def daemon_loop(): + # Could we do this more nicely? The xend.manager functions need access + # to this global state to do their work. + global port_list, notifier, management_interface, mgmt_req_addr, dom0_port + + # Lists of all interfaces, indexed by local event-channel port. + port_list = {} + + xc = Xc.new() + + # Ignore writes to disconnected sockets. We clean up differently. + signal.signal(signal.SIGPIPE, signal.SIG_IGN) + + # Construct the management interface. This is a UNIX domain socket via + # which we receive 'request' datagrams. Each request is a string that + # can be eval'ed as a Python statement. Responses can be remotely eval'ed + # by the requester to create a Python dictionary of result values. + management_interface = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0) + if os.path.exists(CONTROL_DIR+'/'+UNIX_SOCK): + os.unlink(CONTROL_DIR+'/'+UNIX_SOCK) + management_interface.setblocking(False) + management_interface.bind(CONTROL_DIR+'/'+UNIX_SOCK) + + # Interface via which we receive event notifications from other guest + # OSes. This interface also allows us to clear/acknowledge outstanding + # notifications. + notifier = xend.utils.notifier() + + # The DOM0 control interface is not set up via the management interface. + # Note that console messages don't come our way (actually, only driver + # back-ends should use the DOM0 control interface). + dom0_port = xend.utils.port(0) + xend.netif.be_port = dom0_port + xend.blkif.be_port = dom0_port + notifier.bind(dom0_port.local_port) + port_list[dom0_port.local_port] = dom0_port + + ## + ## MAIN LOOP + ## + while 1: + + # Construct a poll set. We wait on: + # 1. Requests on the management interface. + # 2. Incoming event-channel notifications. + # Furthermore, for each active control interface: + # 3. Incoming console data. + # 4. Space for outgoing console data (if there is data to send). + waitset = select.poll() + waitset.register(management_interface, select.POLLIN) + waitset.register(notifier, select.POLLIN) + for idx, con_if in xend.console.interface.list_by_fd.items(): + if not con_if.closed(): + pflags = select.POLLIN + if not con_if.rbuf.empty() and con_if.connected(): + pflags = select.POLLIN | select.POLLOUT + waitset.register(con_if.sock.fileno(), pflags) + + # Wait for something to do... + fdset = waitset.poll() + + # Look for messages on the management interface. + # These should consist of executable Python statements that call + # well-known management functions (e.g., new_control_interface(dom=9)). + try: + data, mgmt_req_addr = management_interface.recvfrom(2048) + except socket.error, error: + if error[0] != errno.EAGAIN: + raise + else: + if mgmt_req_addr: + # Evaluate the request in an exception-trapping sandbox. + try: + print "Mgmt_req[%s]: %s" % (mgmt_req_addr, data) + response = eval('xend.manager.'+data) + + except: + # Catch all exceptions and turn into an error response: + # status: False + # error_type: 'exception' + # exception_type: name of exception type. + # exception value: textual exception value. + exc_type, exc_val = sys.exc_info()[:2] + response = { 'success': False } + response['error_type'] = 'exception' + response['exception_type'] = str(exc_type) + response['exception_value'] = str(exc_val) + response = str(response) + + # Try to send a response to the requester. + if response: + send_management_response(response, mgmt_req_addr) + + # Do work for every console interface that hit in the poll set. + for (fd, events) in fdset: + if xend.console.interface.list_by_fd.has_key(fd): + con_if = xend.console.interface.list_by_fd[fd] + con_if.socket_work() + # We may now have pending data to send via the control + # interface. If so then send all we can and notify the remote. + port = port_list[con_if.key] + if con_if.ctrlif_transmit_work(port): + port.notify() + + # Process control-interface notifications from other guest OSes. + while 1: + # Grab a notification, if there is one. + notification = notifier.read() + if not notification: + break + (idx, type) = notification + + if not port_list.has_key(idx): + continue + + port = port_list[idx] + work_done = False + + con_if = False + if xend.console.interface.list.has_key(idx): + con_if = xend.console.interface.list[idx] + + blk_if = False + if xend.blkif.interface.list.has_key(idx): + blk_if = xend.blkif.interface.list[idx] + + net_if = False + if xend.netif.interface.list.has_key(idx): + net_if = xend.netif.interface.list[idx] + + # If we pick up a disconnect notification then we do any necessary + # cleanup. + if type == notifier.EXCEPTION: + ret = xc.evtchn_status(idx) + if ret['status'] == 'unbound': + notifier.unbind(idx) + del port_list[idx], port + if con_if: + con_if.destroy() + del con_if + if blk_if: + blk_if.destroy() + del blk_if + if net_if: + net_if.destroy() + del net_if + continue + + # Process incoming requests. + while port.request_to_read(): + msg = port.read_request() + work_done = True + type = (msg.get_header())['type'] + if type == CMSG_CONSOLE and con_if: + con_if.ctrlif_rx_req(port, msg) + elif type == CMSG_BLKIF_FE and blk_if: + blk_if.ctrlif_rx_req(port, msg) + elif type == CMSG_BLKIF_BE and port == xend.blkif.be_port: + xend.blkif.backend_rx_req(port, msg) + elif type == CMSG_NETIF_FE and net_if: + net_if.ctrlif_rx_req(port, msg) + elif type == CMSG_NETIF_BE and port == xend.netif.be_port: + xend.netif.backend_rx_req(port, msg) + else: + port.write_response(msg) + + # Process incoming responses. + while port.response_to_read(): + msg = port.read_response() + work_done = True + type = (msg.get_header())['type'] + if type == CMSG_BLKIF_BE and port == xend.blkif.be_port: + xend.blkif.backend_rx_rsp(port, msg) + elif type == CMSG_NETIF_BE and port == xend.netif.be_port: + xend.netif.backend_rx_rsp(port, msg) + + # Send console data. + if con_if and con_if.ctrlif_transmit_work(port): + work_done = True + + # Send blkif messages. + if blk_if and blk_if.ctrlif_transmit_work(port): + work_done = True + + # Send netif messages. + if net_if and net_if.ctrlif_transmit_work(port): + work_done = True + + # Back-end block-device work. + if port == dom0_port and xend.blkif.backend_do_work(port): + work_done = True + + # Back-end network-device work. + if port == xend.netif.be_port and xend.netif.backend_do_work(port): + work_done = True + + # Finally, notify the remote end of any work that we did. + if work_done: + port.notify() + + # Unmask notifications for this port. + notifier.unmask(idx) + + + +def cleanup_daemon(kill=False): + # No cleanup to do if the PID file is empty. + if not os.path.isfile(PID) or not os.path.getsize(PID): + return 0 + # Read the PID of the previous invocation and search active process list. + pid = open(PID, 'r').read() + lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines() + for line in lines: + if re.search('^ *' + pid + '.+xend', line): + if not kill: + print "Daemon is already running (PID %d)" % int(pid) + return 1 + # Old daemon is still active: terminate it. + os.kill(int(pid), 1) + # Delete the, now stale, PID file. + os.remove(PID) + return 0 + + + +def start_daemon(): + if cleanup_daemon(kill=False): + return 1 + + if not os.path.exists(CONTROL_DIR): + os.mkdir(CONTROL_DIR) + + # Open log file. Truncate it if non-empty, and request line buffering. + if os.path.isfile(LOG): + os.rename(LOG, LOG+'.old') + logfile = open(LOG, 'w+', 1) + + # Detach from TTY. + os.setsid() + + # Set the UID. + try: + os.setuid(pwd.getpwnam(USER)[2]) + except KeyError, error: + print "Error: no such user '%s'" % USER + return 1 + + # Ensure that zombie children are automatically reaped. + xend.utils.autoreap() + + # Fork -- parent writes the PID file and exits. + pid = os.fork() + if pid: + pidfile = open(PID, 'w') + pidfile.write(str(pid)) + pidfile.close() + return 0 + + # Close down standard file handles + try: + os.close(0) # stdin + os.close(1) # stdout + os.close(2) # stderr + except: + pass + + # Redirect output to log file, then enter the main loop. + sys.stdout = sys.stderr = logfile + daemon_loop() + return 0 + + + +def stop_daemon(): + return cleanup_daemon(kill=True) diff --git a/tools/xend-old/lib/manager.py b/tools/xend-old/lib/manager.py new file mode 100644 index 0000000000..49517583b5 --- /dev/null +++ b/tools/xend-old/lib/manager.py @@ -0,0 +1,174 @@ + +############################################################# +## xend/manager.py -- Management-interface functions for Xend +## Copyright (c) 2004, K A Fraser (University of Cambridge) +############################################################# + +import xend.blkif, xend.netif, xend.console, xend.main, xend.utils + + +## +## new_control_interface: +## Create a new control interface with the specified domain @dom. +## The console port may also be specified; otherwise a suitable port is +## automatically allocated. +## +def new_control_interface(dom, console_port=-1): + # Allocate an event channel and binbd to it. + port = xend.utils.port(dom) + xend.main.notifier.bind(port.local_port) + + # If necessary, compute a suitable TCP port for console I/O. + if console_port < 0: + console_port = 9600 + port.local_port + + # Create a listening console interface. + con_if = xend.console.interface(console_port, port.local_port) + con_if.listen() + + # Update the master port list. + xend.main.port_list[port.local_port] = port + + # Construct the successful response to be returned to the requester. + response = { 'success': True } + response['local_port'] = port.local_port + response['remote_port'] = port.remote_port + response['console_port'] = console_port + return response + + +## +## new_block_interface: +## Create a new block interface for the specified domain @dom. +## +def new_block_interface(dom, handle=-1): + # By default we create an interface with handle zero. + if handle < 0: + handle = 0 + + # We only support one interface per domain, which must have handle zero. + if handle != 0: + response = { 'success': False } + response['error_type'] = 'Bad handle %d (only handle 0 ' + \ + 'is supported)' % handle + return response + + # Find local event-channel port associated with the specified domain. + port = xend.main.port_from_dom(dom) + if not port: + response = { 'success': False } + response['error_type'] = 'Unknown domain %d' % dom + return response + + # The interface must not already exist. + if xend.blkif.interface.list.has_key(port.local_port): + response = { 'success': False } + response['error_type'] = 'Interface (dom=%d,handle=%d) already ' + \ + 'exists' % (dom, handle) + return response + + # Create the new interface. Initially no virtual devices are attached. + xend.blkif.interface(dom, port.local_port) + + # Response is deferred until back-end driver sends acknowledgement. + return None + + +## +## new_block_device: +## Attach a new virtual block device to the specified block interface +## (@dom, @handle). The new device is identified by @vdev, and maps to +## the real block extent (@pdev, @start_sect, @nr_sect). If @readonly then +## write requests to @vdev will be rejected. +## +def new_block_device(dom, handle, vdev, pdev, start_sect, nr_sect, readonly): + # We only support one interface per domain, which must have handle zero. + if handle != 0: + response = { 'success': False } + response['error_type'] = 'Bad handle %d (only handle 0 ' + \ + 'is supported)' % handle + return response + + # Find local event-channel port associated with the specified domain. + port = xend.main.port_from_dom(dom) + if not port: + response = { 'success': False } + response['error_type'] = 'Unknown domain %d' % dom + return response + + # The interface must exist. + if not xend.blkif.interface.list.has_key(port.local_port): + response = { 'success': False } + response['error_type'] = 'Interface (dom=%d,handle=%d) does not ' + \ + 'exists' % (dom, handle) + return response + + # The virtual device must not yet exist. + blkif = xend.blkif.interface.list[port.local_port] + if not blkif.attach_device(vdev, pdev, start_sect, nr_sect, readonly): + response = { 'success': False } + response['error_type'] = 'Vdevice (dom=%d,handle=%d,vdevice=%d) ' + \ + 'already exists' % (dom, handle, vdev) + return response + + # Response is deferred until back-end driver sends acknowledgement. + return None + + +## +## new_network_interface: +## Create a new network interface for the specified domain @dom. +## +def new_network_interface(dom, handle=-1): + # By default we create an interface with handle zero. + if handle < 0: + handle = 0 + + # We only support one interface per domain, which must have handle zero. + if handle != 0: + response = { 'success': False } + response['error_type'] = 'Bad handle %d (only handle 0 ' + \ + 'is supported)' % handle + return response + + # Find local event-channel port associated with the specified domain. + port = xend.main.port_from_dom(dom) + if not port: + response = { 'success': False } + response['error_type'] = 'Unknown domain %d' % dom + return response + + # The interface must not already exist. + if xend.netif.interface.list.has_key(port.local_port): + response = { 'success': False } + response['error_type'] = 'Interface (dom=%d,handle=%d) already ' + \ + 'exists' % (dom, handle) + return response + + # Create the new interface. Initially no virtual devices are attached. + xend.netif.interface(dom, port.local_port) + + # Response is deferred until back-end driver sends acknowledgement. + return None + +## +## set_network_backend +## Authorise a domain to act as the net backend (assumes we only have one +## backend driver for now). After this call, back end "up" notifications +## for the network will only be accepted from this domain. +## +def set_network_backend(dom): + if xend.netif.be_port.remote_dom != 0: + xend.netif.recovery = True + xend.netif.be_port = xend.main.port_from_dom(dom) + return { 'success' : True } + +## +## set_block_backend +## Authorise a domain to act as the block backend (assumes we only have one +## backend driver for now). After this call, back end "up" notifications +## for the network will only be accepted from this domain. +def set_block_backend(dom): + if xend.blkif.be_port: xend.blkif.recovery = True + xend.blkif.be_port = xend.main.port_from_dom(dom) + return { 'success' : True } diff --git a/tools/xend-old/lib/netif.py b/tools/xend-old/lib/netif.py new file mode 100644 index 0000000000..eaa10086a2 --- /dev/null +++ b/tools/xend-old/lib/netif.py @@ -0,0 +1,187 @@ + +################################################################### +## xend/netif.py -- Network-interface management functions for Xend +## Copyright (c) 2004, K A Fraser (University of Cambridge) +################################################################### + +import errno, random, re, os, select, signal, socket, struct, sys +import xend.main, xend.console, xend.manager, xend.utils, Xc + +CMSG_NETIF_BE = 3 +CMSG_NETIF_FE = 4 +CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED = 0 +CMSG_NETIF_FE_DRIVER_STATUS_CHANGED = 32 +CMSG_NETIF_BE_DRIVER_STATUS_CHANGED = 32 +CMSG_NETIF_FE_INTERFACE_CONNECT = 33 +CMSG_NETIF_FE_INTERFACE_DISCONNECT = 34 +CMSG_NETIF_BE_CREATE = 0 +CMSG_NETIF_BE_DESTROY = 1 +CMSG_NETIF_BE_CONNECT = 2 +CMSG_NETIF_BE_DISCONNECT = 3 + +NETIF_DRIVER_STATUS_DOWN = 0 +NETIF_DRIVER_STATUS_UP = 1 + +pendmsg = None +pendaddr = None + +recovery = False # Is a recovery in progress? +be_port = None # Port object for backend domain + +def backend_tx_req(msg): + if xend.netif.be_port.space_to_write_request(): + xend.netif.be_port.write_request(msg) + xend.netif.be_port.notify() + else: + xend.netif.pendmsg = msg + +def backend_rx_req(port, msg): + port.write_response(msg) + subtype = (msg.get_header())['subtype'] + print "Received netif-be request, subtype %d" % subtype + if subtype == CMSG_NETIF_BE_DRIVER_STATUS_CHANGED: + pl = msg.get_payload() + status = pl['status'] + if status == NETIF_DRIVER_STATUS_UP: + if xend.netif.recovery: + print "New netif backend now UP, notifying guests:" + for netif_key in interface.list.keys(): + netif = interface.list[netif_key] + netif.create() + print " Notifying %d" % netif.dom + msg = xend.utils.message( + CMSG_NETIF_FE, + CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0, + { 'handle' : 0, 'status' : 1 }) + netif.ctrlif_tx_req(xend.main.port_from_dom(netif.dom),msg) + print "Done notifying guests" + recovery = False + else: + print "Unexpected net backend driver status: %d" % status + +def backend_rx_rsp(port, msg): + subtype = (msg.get_header())['subtype'] + print "Received netif-be response, subtype %d" % subtype + if subtype == CMSG_NETIF_BE_CREATE: + rsp = { 'success': True } + xend.main.send_management_response(rsp, xend.netif.pendaddr) + elif subtype == CMSG_NETIF_BE_CONNECT: + pl = msg.get_payload() + (dom, hnd, evtchn, tx_frame, rx_frame, st) = ( + pl['domid'], pl['netif_handle'], pl['evtchn'], + pl['tx_shmem_frame'], pl['rx_shmem_frame'], pl['status']) + netif = interface.list[xend.main.port_from_dom(dom).local_port] + msg = xend.utils.message(CMSG_NETIF_FE, + CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0, + { 'handle' : 0, 'status' : 2, + 'evtchn' : netif.evtchn['port2'], + 'mac[0]' : netif.mac[0], + 'mac[1]' : netif.mac[1], + 'mac[2]' : netif.mac[2], + 'mac[3]' : netif.mac[3], + 'mac[4]' : netif.mac[4], + 'mac[5]' : netif.mac[5] }) + netif.ctrlif_tx_req(xend.main.port_list[netif.key], msg) + +def backend_do_work(port): + global pendmsg + if pendmsg and port.space_to_write_request(): + port.write_request(pendmsg) + pendmsg = None + return True + return False + + +class interface: + + # Dictionary of all network-device interfaces. + list = {} + + drvdom = None + + # NB. 'key' is an opaque value that has no meaning in this class. + def __init__(self, dom, key): + self.dom = dom + self.key = key + self.pendmsg = None + + # VIFs get a random MAC address with a "special" vendor id. + # + # NB. The vendor is currently an "obsolete" one that used to belong + # to DEC (AA-00-00). Using it is probably a bit rude :-) + # + # NB2. The first bit of the first random octet is set to zero for + # all dynamic MAC addresses. This may allow us to manually specify + # MAC addresses for some VIFs with no fear of clashes. + self.mac = [ 0xaa, 0x00, 0x00 ] + self.mac.append(int(random.random()*128)) + self.mac.append(int(random.random()*256)) + self.mac.append(int(random.random()*256)) + + interface.list[key] = self + self.create() + + def create(self): + """Notify the current network back end to create the virtual interface + represented by this object.""" + msg = xend.utils.message(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE, 0, + { 'domid' : self.dom, 'netif_handle' : 0, + 'mac[0]' : self.mac[0], + 'mac[1]' : self.mac[1], + 'mac[2]' : self.mac[2], + 'mac[3]' : self.mac[3], + 'mac[4]' : self.mac[4], + 'mac[5]' : self.mac[5] }) + xend.netif.pendaddr = xend.main.mgmt_req_addr + backend_tx_req(msg) + + + # Completely destroy this interface. + def destroy(self): + del interface.list[self.key] + msg = xend.utils.message(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY, 0, + { 'domid' : self.dom, 'netif_handle' : 0 }) + backend_tx_req(msg) + + + # The parameter @port is the control-interface event channel. This method + # returns True if messages were written to the control interface. + def ctrlif_transmit_work(self, port): + if self.pendmsg and port.space_to_write_request(): + port.write_request(self.pendmsg) + self.pendmsg = None + return True + return False + + def ctrlif_tx_req(self, port, msg): + if port.space_to_write_request(): + port.write_request(msg) + port.notify() + else: + self.pendmsg = msg + + def ctrlif_rx_req(self, port, msg): + port.write_response(msg) + subtype = (msg.get_header())['subtype'] + if subtype == CMSG_NETIF_FE_DRIVER_STATUS_CHANGED: + print "netif driver up message from %d" % port.remote_dom + msg = xend.utils.message(CMSG_NETIF_FE, + CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0, + { 'handle' : 0, 'status' : 1 }) + self.ctrlif_tx_req(port, msg) + elif subtype == CMSG_NETIF_FE_INTERFACE_CONNECT: + print "netif connect request from %d" % port.remote_dom + pl = msg.get_payload() + (hnd, tx_frame, rx_frame) = (pl['handle'], pl['tx_shmem_frame'], + pl['rx_shmem_frame']) + xc = Xc.new() + self.evtchn = xc.evtchn_bind_interdomain( + dom1=xend.netif.be_port.remote_dom, + dom2=self.dom) + msg = xend.utils.message(CMSG_NETIF_BE, + CMSG_NETIF_BE_CONNECT, 0, + { 'domid' : self.dom, 'netif_handle' : 0, + 'tx_shmem_frame' : tx_frame, + 'rx_shmem_frame' : rx_frame, + 'evtchn' : self.evtchn['port1'] }) + backend_tx_req(msg) diff --git a/tools/xend-old/lib/utils.c b/tools/xend-old/lib/utils.c new file mode 100644 index 0000000000..2d96577203 --- /dev/null +++ b/tools/xend-old/lib/utils.c @@ -0,0 +1,1384 @@ +/****************************************************************************** + * utils.c + * + * Copyright (c) 2004, K A Fraser + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "domain_controller.h" + +#include + +/* Needed for Python versions earlier than 2.3. */ +#ifndef PyMODINIT_FUNC +#define PyMODINIT_FUNC DL_EXPORT(void) +#endif + +/* NB. The following should be kept in sync with the kernel's evtchn driver. */ +#define EVTCHN_DEV_NAME "/dev/xen/evtchn" +#define EVTCHN_DEV_MAJOR 10 +#define EVTCHN_DEV_MINOR 200 +#define PORT_NORMAL 0x0000 /* A standard event notification. */ +#define PORT_EXCEPTION 0x8000 /* An exceptional notification. */ +#define PORTIDX_MASK 0x7fff /* Strip subtype to obtain port index. */ +/* /dev/xen/evtchn ioctls: */ +/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */ +#define EVTCHN_RESET _IO('E', 1) +/* EVTCHN_BIND: Bind to teh specified event-channel port. */ +#define EVTCHN_BIND _IO('E', 2) +/* EVTCHN_UNBIND: Unbind from the specified event-channel port. */ +#define EVTCHN_UNBIND _IO('E', 3) + +/* Size of a machine page frame. */ +#define PAGE_SIZE 4096 + + +/* + * *********************** NOTIFIER *********************** + */ + +typedef struct { + PyObject_HEAD; + int evtchn_fd; +} xu_notifier_object; + +static PyObject *xu_notifier_read(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + u16 v; + int bytes; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + while ( (bytes = read(xun->evtchn_fd, &v, sizeof(v))) == -1 ) + { + if ( errno == EINTR ) + continue; + if ( errno == EAGAIN ) + goto none; + return PyErr_SetFromErrno(PyExc_IOError); + } + + if ( bytes == sizeof(v) ) + return Py_BuildValue("(i,i)", v&PORTIDX_MASK, v&~PORTIDX_MASK); + + none: + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_unmask(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + u16 v; + int idx; + + if ( !PyArg_ParseTuple(args, "i", &idx) ) + return NULL; + + v = (u16)idx; + + (void)write(xun->evtchn_fd, &v, sizeof(v)); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_bind(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + int idx; + + if ( !PyArg_ParseTuple(args, "i", &idx) ) + return NULL; + + if ( ioctl(xun->evtchn_fd, EVTCHN_BIND, idx) != 0 ) + return PyErr_SetFromErrno(PyExc_IOError); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_unbind(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + int idx; + + if ( !PyArg_ParseTuple(args, "i", &idx) ) + return NULL; + + if ( ioctl(xun->evtchn_fd, EVTCHN_UNBIND, idx) != 0 ) + return PyErr_SetFromErrno(PyExc_IOError); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + return PyInt_FromLong(xun->evtchn_fd); +} + +static PyMethodDef xu_notifier_methods[] = { + { "read", + (PyCFunction)xu_notifier_read, + METH_VARARGS, + "Read a (@port, @type) pair.\n" }, + + { "unmask", + (PyCFunction)xu_notifier_unmask, + METH_VARARGS, + "Unmask notifications for a @port.\n" }, + + { "bind", + (PyCFunction)xu_notifier_bind, + METH_VARARGS, + "Get notifications for a @port.\n" }, + + { "unbind", + (PyCFunction)xu_notifier_unbind, + METH_VARARGS, + "No longer get notifications for a @port.\n" }, + + { "fileno", + (PyCFunction)xu_notifier_fileno, + METH_VARARGS, + "Return the file descriptor for the notification channel.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_notifier_type; + +static PyObject *xu_notifier_new(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + xun = PyObject_New(xu_notifier_object, &xu_notifier_type); + + reopen: + xun->evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR); + if ( xun->evtchn_fd == -1 ) + { + if ( (errno == ENOENT) && + ((mkdir("/dev/xen", 0755) == 0) || (errno == EEXIST)) && + (mknod(EVTCHN_DEV_NAME, S_IFCHR|0600, + (EVTCHN_DEV_MAJOR << 8) | EVTCHN_DEV_MINOR) == 0) ) + goto reopen; + PyObject_Del((PyObject *)xun); + return PyErr_SetFromErrno(PyExc_IOError); + } + + return (PyObject *)xun; +} + +static PyObject *xu_notifier_getattr(PyObject *obj, char *name) +{ + if ( strcmp(name, "EXCEPTION") == 0 ) + return PyInt_FromLong(PORT_EXCEPTION); + if ( strcmp(name, "NORMAL") == 0 ) + return PyInt_FromLong(PORT_NORMAL); + return Py_FindMethod(xu_notifier_methods, obj, name); +} + +static void xu_notifier_dealloc(PyObject *self) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + (void)close(xun->evtchn_fd); + PyObject_Del(self); +} + +static PyTypeObject xu_notifier_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "notifier", + sizeof(xu_notifier_object), + 0, + xu_notifier_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_notifier_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** MESSAGE *********************** + */ + +#define TYPE(_x,_y) (((_x)<<8)|(_y)) +#define P2C(_struct, _field, _ctype) \ + do { \ + PyObject *obj; \ + if ( (obj = PyDict_GetItemString(payload, #_field)) != NULL ) \ + { \ + if ( PyInt_Check(obj) ) \ + { \ + ((_struct *)&xum->msg.msg[0])->_field = \ + (_ctype)PyInt_AsLong(obj); \ + dict_items_parsed++; \ + } \ + else if ( PyLong_Check(obj) ) \ + { \ + ((_struct *)&xum->msg.msg[0])->_field = \ + (_ctype)PyLong_AsUnsignedLongLong(obj); \ + dict_items_parsed++; \ + } \ + } \ + xum->msg.length = sizeof(_struct); \ + } while ( 0 ) +#define C2P(_struct, _field, _pytype, _ctype) \ + do { \ + PyObject *obj = Py ## _pytype ## _From ## _ctype \ + (((_struct *)&xum->msg.msg[0])->_field); \ + if ( dict == NULL ) dict = PyDict_New(); \ + PyDict_SetItemString(dict, #_field, obj); \ + } while ( 0 ) + +typedef struct { + PyObject_HEAD; + control_msg_t msg; +} xu_message_object; + +static PyObject *xu_message_append_payload(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + char *str; + int len; + + if ( !PyArg_ParseTuple(args, "s#", &str, &len) ) + return NULL; + + if ( (len + xum->msg.length) > sizeof(xum->msg.msg) ) + { + PyErr_SetString(PyExc_RuntimeError, "out of space in control message"); + return NULL; + } + + memcpy(&xum->msg.msg[xum->msg.length], str, len); + xum->msg.length += len; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_message_set_response_fields(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + PyObject *payload; + int dict_items_parsed = 0; + + if ( !PyArg_ParseTuple(args, "O", &payload) ) + return NULL; + + if ( !PyDict_Check(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload is not a dictionary"); + return NULL; + } + + switch ( TYPE(xum->msg.type, xum->msg.subtype) ) + { + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED): + P2C(blkif_fe_driver_status_changed_t, nr_interfaces, u32); + break; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): + P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32); + break; + } + + if ( dict_items_parsed != PyDict_Size(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload contains bad items"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_message_get_payload(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + PyObject *dict = NULL; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + switch ( TYPE(xum->msg.type, xum->msg.subtype) ) + { + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED): + C2P(blkif_fe_interface_status_changed_t, handle, Int, Long); + C2P(blkif_fe_interface_status_changed_t, status, Int, Long); + C2P(blkif_fe_interface_status_changed_t, evtchn, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED): + C2P(blkif_fe_driver_status_changed_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT): + C2P(blkif_fe_interface_connect_t, handle, Int, Long); + C2P(blkif_fe_interface_connect_t, shmem_frame, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_DISCONNECT): + C2P(blkif_fe_interface_disconnect_t, handle, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE): + C2P(blkif_be_create_t, domid, Int, Long); + C2P(blkif_be_create_t, blkif_handle, Int, Long); + C2P(blkif_be_create_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY): + C2P(blkif_be_destroy_t, domid, Int, Long); + C2P(blkif_be_destroy_t, blkif_handle, Int, Long); + C2P(blkif_be_destroy_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT): + C2P(blkif_be_connect_t, domid, Int, Long); + C2P(blkif_be_connect_t, blkif_handle, Int, Long); + C2P(blkif_be_connect_t, shmem_frame, Int, Long); + C2P(blkif_be_connect_t, evtchn, Int, Long); + C2P(blkif_be_connect_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT): + C2P(blkif_be_disconnect_t, domid, Int, Long); + C2P(blkif_be_disconnect_t, blkif_handle, Int, Long); + C2P(blkif_be_disconnect_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE): + C2P(blkif_be_vbd_create_t, domid, Int, Long); + C2P(blkif_be_vbd_create_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_create_t, vdevice, Int, Long); + C2P(blkif_be_vbd_create_t, readonly, Int, Long); + C2P(blkif_be_vbd_create_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY): + C2P(blkif_be_vbd_destroy_t, domid, Int, Long); + C2P(blkif_be_vbd_destroy_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_destroy_t, vdevice, Int, Long); + C2P(blkif_be_vbd_destroy_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW): + C2P(blkif_be_vbd_grow_t, domid, Int, Long); + C2P(blkif_be_vbd_grow_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_grow_t, vdevice, Int, Long); + C2P(blkif_be_vbd_grow_t, extent.sector_start, + Long, UnsignedLongLong); + C2P(blkif_be_vbd_grow_t, extent.sector_length, + Long, UnsignedLongLong); + C2P(blkif_be_vbd_grow_t, extent.device, Int, Long); + C2P(blkif_be_vbd_grow_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_SHRINK): + C2P(blkif_be_vbd_shrink_t, domid, Int, Long); + C2P(blkif_be_vbd_shrink_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_shrink_t, vdevice, Int, Long); + C2P(blkif_be_vbd_shrink_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED): + C2P(blkif_be_driver_status_changed_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED): + C2P(netif_fe_interface_status_changed_t, handle, Int, Long); + C2P(netif_fe_interface_status_changed_t, status, Int, Long); + C2P(netif_fe_interface_status_changed_t, evtchn, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): + C2P(netif_fe_driver_status_changed_t, status, Int, Long); + C2P(netif_fe_driver_status_changed_t, nr_interfaces, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT): + C2P(netif_fe_interface_connect_t, handle, Int, Long); + C2P(netif_fe_interface_connect_t, tx_shmem_frame, Int, Long); + C2P(netif_fe_interface_connect_t, rx_shmem_frame, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_DISCONNECT): + C2P(netif_fe_interface_disconnect_t, handle, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE): + C2P(netif_be_create_t, domid, Int, Long); + C2P(netif_be_create_t, netif_handle, Int, Long); + C2P(netif_be_create_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY): + C2P(netif_be_destroy_t, domid, Int, Long); + C2P(netif_be_destroy_t, netif_handle, Int, Long); + C2P(netif_be_destroy_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT): + C2P(netif_be_connect_t, domid, Int, Long); + C2P(netif_be_connect_t, netif_handle, Int, Long); + C2P(netif_be_connect_t, tx_shmem_frame, Int, Long); + C2P(netif_be_connect_t, rx_shmem_frame, Int, Long); + C2P(netif_be_connect_t, evtchn, Int, Long); + C2P(netif_be_connect_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT): + C2P(netif_be_disconnect_t, domid, Int, Long); + C2P(netif_be_disconnect_t, netif_handle, Int, Long); + C2P(netif_be_disconnect_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED): + C2P(netif_be_driver_status_changed_t, status, Int, Long); + return dict; + } + + return PyString_FromStringAndSize(xum->msg.msg, xum->msg.length); +} + +static PyObject *xu_message_get_header(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + return Py_BuildValue("{s:i,s:i,s:i}", + "type", xum->msg.type, + "subtype", xum->msg.subtype, + "id", xum->msg.id); +} + +static PyMethodDef xu_message_methods[] = { + { "append_payload", + (PyCFunction)xu_message_append_payload, + METH_VARARGS, + "Append @str to the message payload.\n" }, + + { "set_response_fields", + (PyCFunction)xu_message_set_response_fields, + METH_VARARGS, + "Fill in the response fields in a message that was passed to us.\n" }, + + { "get_payload", + (PyCFunction)xu_message_get_payload, + METH_VARARGS, + "Return the message payload in string form.\n" }, + + { "get_header", + (PyCFunction)xu_message_get_header, + METH_VARARGS, + "Returns a dictionary of values for @type, @subtype, and @id.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_message_type; + +static PyObject *xu_message_new(PyObject *self, PyObject *args) +{ + xu_message_object *xum; + int type, subtype, id, dict_items_parsed = 0; + PyObject *payload = NULL; + + if ( !PyArg_ParseTuple(args, "iii|O", &type, &subtype, &id, &payload) ) + return NULL; + + xum = PyObject_New(xu_message_object, &xu_message_type); + + xum->msg.type = type; + xum->msg.subtype = subtype; + xum->msg.id = id; + xum->msg.length = 0; + + if ( payload == NULL ) + return (PyObject *)xum; + + if ( !PyDict_Check(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload is not a dictionary"); + PyObject_Del((PyObject *)xum); + return NULL; + } + + switch ( TYPE(type, subtype) ) + { + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED): + P2C(blkif_fe_interface_status_changed_t, handle, u32); + P2C(blkif_fe_interface_status_changed_t, status, u32); + P2C(blkif_fe_interface_status_changed_t, evtchn, u16); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE): + P2C(blkif_be_create_t, domid, u32); + P2C(blkif_be_create_t, blkif_handle, u32); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY): + P2C(blkif_be_destroy_t, domid, u32); + P2C(blkif_be_destroy_t, blkif_handle, u32); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT): + P2C(blkif_be_connect_t, domid, u32); + P2C(blkif_be_connect_t, blkif_handle, u32); + P2C(blkif_be_connect_t, shmem_frame, memory_t); + P2C(blkif_be_connect_t, evtchn, u16); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT): + P2C(blkif_be_disconnect_t, domid, u32); + P2C(blkif_be_disconnect_t, blkif_handle, u32); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE): + P2C(blkif_be_vbd_create_t, domid, u32); + P2C(blkif_be_vbd_create_t, blkif_handle, u32); + P2C(blkif_be_vbd_create_t, vdevice, blkif_vdev_t); + P2C(blkif_be_vbd_create_t, readonly, u16); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY): + P2C(blkif_be_vbd_destroy_t, domid, u32); + P2C(blkif_be_vbd_destroy_t, blkif_handle, u32); + P2C(blkif_be_vbd_destroy_t, vdevice, blkif_vdev_t); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW): + P2C(blkif_be_vbd_grow_t, domid, u32); + P2C(blkif_be_vbd_grow_t, blkif_handle, u32); + P2C(blkif_be_vbd_grow_t, vdevice, blkif_vdev_t); + P2C(blkif_be_vbd_grow_t, extent.sector_start, blkif_sector_t); + P2C(blkif_be_vbd_grow_t, extent.sector_length, blkif_sector_t); + P2C(blkif_be_vbd_grow_t, extent.device, blkif_pdev_t); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_SHRINK): + P2C(blkif_be_vbd_shrink_t, domid, u32); + P2C(blkif_be_vbd_shrink_t, blkif_handle, u32); + P2C(blkif_be_vbd_shrink_t, vdevice, blkif_vdev_t); + break; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED): + P2C(netif_fe_interface_status_changed_t, handle, u32); + P2C(netif_fe_interface_status_changed_t, status, u32); + P2C(netif_fe_interface_status_changed_t, evtchn, u16); + P2C(netif_fe_interface_status_changed_t, mac[0], u8); + P2C(netif_fe_interface_status_changed_t, mac[1], u8); + P2C(netif_fe_interface_status_changed_t, mac[2], u8); + P2C(netif_fe_interface_status_changed_t, mac[3], u8); + P2C(netif_fe_interface_status_changed_t, mac[4], u8); + P2C(netif_fe_interface_status_changed_t, mac[5], u8); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE): + P2C(netif_be_create_t, domid, u32); + P2C(netif_be_create_t, netif_handle, u32); + P2C(netif_be_create_t, mac[0], u8); + P2C(netif_be_create_t, mac[1], u8); + P2C(netif_be_create_t, mac[2], u8); + P2C(netif_be_create_t, mac[3], u8); + P2C(netif_be_create_t, mac[4], u8); + P2C(netif_be_create_t, mac[5], u8); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY): + P2C(netif_be_destroy_t, domid, u32); + P2C(netif_be_destroy_t, netif_handle, u32); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT): + P2C(netif_be_connect_t, domid, u32); + P2C(netif_be_connect_t, netif_handle, u32); + P2C(netif_be_connect_t, tx_shmem_frame, memory_t); + P2C(netif_be_connect_t, rx_shmem_frame, memory_t); + P2C(netif_be_connect_t, evtchn, u16); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT): + P2C(netif_be_disconnect_t, domid, u32); + P2C(netif_be_disconnect_t, netif_handle, u32); + break; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): + P2C(netif_fe_driver_status_changed_t, status, u32); + P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32); + break; + } + + if ( dict_items_parsed != PyDict_Size(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload contains bad items"); + PyObject_Del((PyObject *)xum); + return NULL; + } + + return (PyObject *)xum; +} + +static PyObject *xu_message_getattr(PyObject *obj, char *name) +{ + xu_message_object *xum; + if ( strcmp(name, "MAX_PAYLOAD") == 0 ) + return PyInt_FromLong(sizeof(xum->msg.msg)); + return Py_FindMethod(xu_message_methods, obj, name); +} + +static void xu_message_dealloc(PyObject *self) +{ + PyObject_Del(self); +} + +static PyTypeObject xu_message_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "message", + sizeof(xu_message_object), + 0, + xu_message_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_message_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** PORT *********************** + */ + +static control_if_t *map_control_interface(int fd, unsigned long pfn) +{ + char *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, + MAP_SHARED, fd, pfn * PAGE_SIZE); + if ( vaddr == MAP_FAILED ) + return NULL; + return (control_if_t *)(vaddr + 2048); +} +static void unmap_control_interface(int fd, control_if_t *c) +{ + char *vaddr = (char *)c - 2048; + (void)munmap(vaddr, PAGE_SIZE); +} + +typedef struct xu_port_object { + PyObject_HEAD; + int mem_fd; + int xc_handle; + u32 remote_dom; + int local_port, remote_port; + control_if_t *interface; + CONTROL_RING_IDX tx_req_cons, tx_resp_prod; + CONTROL_RING_IDX rx_req_prod, rx_resp_cons; +} xu_port_object; + +static PyObject *port_error; + +static int xup_connect(xu_port_object *xup, domid_t dom, + int local_port, int remote_port){ + // From our prespective rx = producer, tx = consumer. + int err = 0; + printf("%s> dom=%u %d:%d\n", __FUNCTION__, (unsigned int)dom, + local_port, remote_port); + + // Consumer = tx. + //xup->interface->tx_resp_prod = 0; + //xup->interface->tx_req_prod = 0; + xup->tx_resp_prod = xup->interface->tx_resp_prod; + xup->tx_req_cons = xup->interface->tx_resp_prod; + printf("%s> tx: %u %u : %u %u\n", __FUNCTION__, + (unsigned int)xup->interface->tx_resp_prod, + (unsigned int)xup->tx_resp_prod, + (unsigned int)xup->tx_req_cons, + (unsigned int)xup->interface->tx_req_prod); + + // Producer = rx. + //xup->interface->rx_req_prod = 0; + //xup->interface->rx_resp_prod = 0; + xup->rx_req_prod = xup->interface->rx_req_prod; + xup->rx_resp_cons = xup->interface->rx_resp_prod; + printf("%s> rx: %u %u : %u %u\n", __FUNCTION__, + (unsigned int)xup->rx_resp_cons, + (unsigned int)xup->interface->rx_resp_prod, + (unsigned int)xup->interface->rx_req_prod, + (unsigned int)xup->rx_req_prod); + + xup->remote_dom = dom; + xup->local_port = local_port; + xup->remote_port = remote_port; + + printf("%s< err=%d\n", __FUNCTION__, err); + return err; +} + +static PyObject *xu_port_notify(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + (void)xc_evtchn_send(xup->xc_handle, xup->local_port); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_port_read_request(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX c = xup->tx_req_cons; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->tx_req_prod) || + ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) ) + { + PyErr_SetString(port_error, "no request to read"); + return NULL; + } + + cmsg = &cif->tx_ring[MASK_CONTROL_IDX(c)]; + xum = PyObject_New(xu_message_object, &xu_message_type); + memcpy(&xum->msg, cmsg, sizeof(*cmsg)); + if ( xum->msg.length > sizeof(xum->msg.msg) ) + xum->msg.length = sizeof(xum->msg.msg); + xup->tx_req_cons++; + return (PyObject *)xum; +} + +static PyObject *xu_port_write_request(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX p = xup->rx_req_prod; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) ) + return NULL; + + if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) ) + { + PyErr_SetString(PyExc_TypeError, "expected a xend.utils.message"); + return NULL; + } + + if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) ) + { + PyErr_SetString(port_error, "no space to write request"); + return NULL; + } + + cmsg = &cif->rx_ring[MASK_CONTROL_IDX(p)]; + memcpy(cmsg, &xum->msg, sizeof(*cmsg)); + + xup->rx_req_prod = cif->rx_req_prod = p + 1; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_port_read_response(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX c = xup->rx_resp_cons; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) ) + { + PyErr_SetString(port_error, "no response to read"); + return NULL; + } + + cmsg = &cif->rx_ring[MASK_CONTROL_IDX(c)]; + xum = PyObject_New(xu_message_object, &xu_message_type); + memcpy(&xum->msg, cmsg, sizeof(*cmsg)); + if ( xum->msg.length > sizeof(xum->msg.msg) ) + xum->msg.length = sizeof(xum->msg.msg); + xup->rx_resp_cons++; + return (PyObject *)xum; +} + +static PyObject *xu_port_write_response(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX p = xup->tx_resp_prod; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) ) + return NULL; + + if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) ) + { + PyErr_SetString(PyExc_TypeError, "expected a xend.utils.message"); + return NULL; + } + + if ( p == xup->tx_req_cons ) + { + PyErr_SetString(port_error, "no space to write response"); + return NULL; + } + + cmsg = &cif->tx_ring[MASK_CONTROL_IDX(p)]; + memcpy(cmsg, &xum->msg, sizeof(*cmsg)); + + xup->tx_resp_prod = cif->tx_resp_prod = p + 1; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_port_request_to_read(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX c = xup->tx_req_cons; + control_if_t *cif = xup->interface; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->tx_req_prod) || + ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyObject *xu_port_space_to_write_request(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX p = xup->rx_req_prod; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX c = xup->rx_resp_cons; + control_if_t *cif = xup->interface; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyObject *xu_port_space_to_write_response( + PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX p = xup->tx_resp_prod; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( p == xup->tx_req_cons ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyMethodDef xu_port_methods[] = { + { "notify", + (PyCFunction)xu_port_notify, + METH_VARARGS, + "Send a notification to the remote end.\n" }, + + { "read_request", + (PyCFunction)xu_port_read_request, + METH_VARARGS, + "Read a request message from the control interface.\n" }, + + { "write_request", + (PyCFunction)xu_port_write_request, + METH_VARARGS, + "Write a request message to the control interface.\n" }, + + { "read_response", + (PyCFunction)xu_port_read_response, + METH_VARARGS, + "Read a response message from the control interface.\n" }, + + { "write_response", + (PyCFunction)xu_port_write_response, + METH_VARARGS, + "Write a response message to the control interface.\n" }, + + { "request_to_read", + (PyCFunction)xu_port_request_to_read, + METH_VARARGS, + "Returns TRUE if there is a request message to read.\n" }, + + { "space_to_write_request", + (PyCFunction)xu_port_space_to_write_request, + METH_VARARGS, + "Returns TRUE if there is space to write a request message.\n" }, + + { "response_to_read", + (PyCFunction)xu_port_response_to_read, + METH_VARARGS, + "Returns TRUE if there is a response message to read.\n" }, + + { "space_to_write_response", + (PyCFunction)xu_port_space_to_write_response, + METH_VARARGS, + "Returns TRUE if there is space to write a response message.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_port_type; + +static PyObject *xu_port_new(PyObject *self, PyObject *args) +{ + xu_port_object *xup; + u32 dom; + int port1, port2; + xc_dominfo_t info; + + if ( !PyArg_ParseTuple(args, "i", &dom) ) + return NULL; + + xup = PyObject_New(xu_port_object, &xu_port_type); + + if ( (xup->mem_fd = open("/dev/mem", O_RDWR)) == -1 ) + { + PyErr_SetString(port_error, "Could not open '/dev/mem'"); + goto fail1; + } + + /* Set the General-Purpose Subject whose page frame will be mapped. */ + (void)ioctl(xup->mem_fd, _IO('M', 1), (unsigned long)dom); + + if ( (xup->xc_handle = xc_interface_open()) == -1 ) + { + PyErr_SetString(port_error, "Could not open Xen control interface"); + goto fail2; + } + + if ( dom == 0 ) + { + /* + * The control-interface event channel for DOM0 is already set up. + * We use an ioctl to discover the port at our end of the channel. + */ + port1 = ioctl(xup->xc_handle, IOCTL_PRIVCMD_INITDOMAIN_EVTCHN, NULL); + port2 = -1; /* We don't need the remote end of the DOM0 link. */ + if ( port1 < 0 ) + { + PyErr_SetString(port_error, "Could not open channel to DOM0"); + goto fail3; + } + } + else if ( xc_evtchn_bind_interdomain(xup->xc_handle, + DOMID_SELF, dom, + &port1, &port2) != 0 ) + { + PyErr_SetString(port_error, "Could not open channel to domain"); + goto fail3; + } + + if ( (xc_domain_getinfo(xup->xc_handle, dom, 1, &info) != 1) || + (info.domid != dom) ) + { + PyErr_SetString(port_error, "Failed to obtain domain status"); + goto fail4; + } + + xup->interface = + map_control_interface(xup->mem_fd, info.shared_info_frame); + if ( xup->interface == NULL ) + { + PyErr_SetString(port_error, "Failed to map domain control interface"); + goto fail4; + } + + xup_connect(xup, dom, port1, port2); + return (PyObject *)xup; + + + fail4: + (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, port1); + fail3: + (void)xc_interface_close(xup->xc_handle); + fail2: + (void)close(xup->mem_fd); + fail1: + PyObject_Del((PyObject *)xup); + return NULL; +} + +static PyObject *xu_port_getattr(PyObject *obj, char *name) +{ + xu_port_object *xup = (xu_port_object *)obj; + if ( strcmp(name, "local_port") == 0 ) + return PyInt_FromLong(xup->local_port); + if ( strcmp(name, "remote_port") == 0 ) + return PyInt_FromLong(xup->remote_port); + if ( strcmp(name, "remote_dom") == 0 ) + return PyInt_FromLong(xup->remote_dom); + return Py_FindMethod(xu_port_methods, obj, name); +} + +static void xu_port_dealloc(PyObject *self) +{ + xu_port_object *xup = (xu_port_object *)self; + unmap_control_interface(xup->mem_fd, xup->interface); + if ( xup->remote_dom != 0 ) + (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, xup->local_port); + (void)xc_interface_close(xup->xc_handle); + (void)close(xup->mem_fd); + PyObject_Del(self); +} + +static PyTypeObject xu_port_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "port", + sizeof(xu_port_object), + 0, + xu_port_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_port_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** BUFFER *********************** + */ + +#define BUFSZ 65536 +#define MASK_BUF_IDX(_i) ((_i)&(BUFSZ-1)) +typedef unsigned int BUF_IDX; + +typedef struct { + PyObject_HEAD; + char *buf; + unsigned int prod, cons; +} xu_buffer_object; + +static PyObject *__xu_buffer_peek(xu_buffer_object *xub, int max) +{ + PyObject *str1, *str2; + int len1, len2, c = MASK_BUF_IDX(xub->cons); + + len1 = xub->prod - xub->cons; + if ( len1 > (BUFSZ - c) ) /* clip to ring wrap */ + len1 = BUFSZ - c; + if ( len1 > max ) /* clip to specified maximum */ + len1 = max; + if ( len1 < 0 ) /* sanity */ + len1 = 0; + + if ( (str1 = PyString_FromStringAndSize(&xub->buf[c], len1)) == NULL ) + return NULL; + + if ( (len1 < (xub->prod - xub->cons)) && (len1 < max) ) + { + len2 = max - len1; + if ( len2 > MASK_BUF_IDX(xub->prod) ) + len2 = MASK_BUF_IDX(xub->prod); + if ( len2 > 0 ) + { + str2 = PyString_FromStringAndSize(&xub->buf[0], len2); + if ( str2 == NULL ) + return NULL; + PyString_ConcatAndDel(&str1, str2); + if ( str1 == NULL ) + return NULL; + } + } + + return str1; +} + +static PyObject *xu_buffer_peek(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + int max = 1024; + + if ( !PyArg_ParseTuple(args, "|i", &max) ) + return NULL; + + return __xu_buffer_peek(xub, max); +} + +static PyObject *xu_buffer_read(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + PyObject *str; + int max = 1024; + + if ( !PyArg_ParseTuple(args, "|i", &max) ) + return NULL; + + if ( (str = __xu_buffer_peek(xub, max)) != NULL ) + xub->cons += PyString_Size(str); + + return str; +} + +static PyObject *xu_buffer_discard(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + int max, len; + + if ( !PyArg_ParseTuple(args, "i", &max) ) + return NULL; + + len = xub->prod - xub->cons; + if ( len > max ) + len = max; + if ( len < 0 ) + len = 0; + + xub->cons += len; + + return PyInt_FromLong(len); +} + +static PyObject *xu_buffer_write(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + char *str; + int len, len1, len2; + + if ( !PyArg_ParseTuple(args, "s#", &str, &len) ) + return NULL; + + len1 = len; + if ( len1 > (BUFSZ - MASK_BUF_IDX(xub->prod)) ) + len1 = BUFSZ - MASK_BUF_IDX(xub->prod); + if ( len1 > (BUFSZ - (xub->prod - xub->cons)) ) + len1 = BUFSZ - (xub->prod - xub->cons); + + if ( len1 == 0 ) + return PyInt_FromLong(0); + + memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[0], len1); + xub->prod += len1; + + if ( len1 < len ) + { + len2 = len - len1; + if ( len2 > (BUFSZ - MASK_BUF_IDX(xub->prod)) ) + len2 = BUFSZ - MASK_BUF_IDX(xub->prod); + if ( len2 > (BUFSZ - (xub->prod - xub->cons)) ) + len2 = BUFSZ - (xub->prod - xub->cons); + if ( len2 != 0 ) + { + memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[len1], len2); + xub->prod += len2; + return PyInt_FromLong(len1 + len2); + } + } + + return PyInt_FromLong(len1); +} + +static PyObject *xu_buffer_empty(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( xub->cons == xub->prod ) + return PyInt_FromLong(1); + + return PyInt_FromLong(0); +} + +static PyObject *xu_buffer_full(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (xub->prod - xub->cons) == BUFSZ ) + return PyInt_FromLong(1); + + return PyInt_FromLong(0); +} + +static PyMethodDef xu_buffer_methods[] = { + { "peek", + (PyCFunction)xu_buffer_peek, + METH_VARARGS, + "Peek up to @max bytes from the buffer. Returns a string.\n" }, + + { "read", + (PyCFunction)xu_buffer_read, + METH_VARARGS, + "Read up to @max bytes from the buffer. Returns a string.\n" }, + + { "discard", + (PyCFunction)xu_buffer_discard, + METH_VARARGS, + "Discard up to @max bytes from the buffer. Returns number of bytes.\n" }, + + { "write", + (PyCFunction)xu_buffer_write, + METH_VARARGS, + "Write @string into buffer. Return number of bytes written.\n" }, + + { "empty", + (PyCFunction)xu_buffer_empty, + METH_VARARGS, + "Return TRUE if the buffer is empty.\n" }, + + { "full", + (PyCFunction)xu_buffer_full, + METH_VARARGS, + "Return TRUE if the buffer is full.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_buffer_type; + +static PyObject *xu_buffer_new(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + xub = PyObject_New(xu_buffer_object, &xu_buffer_type); + + if ( (xub->buf = malloc(BUFSZ)) == NULL ) + { + PyObject_Del((PyObject *)xub); + return NULL; + } + + xub->prod = xub->cons = 0; + + return (PyObject *)xub; +} + +static PyObject *xu_buffer_getattr(PyObject *obj, char *name) +{ + return Py_FindMethod(xu_buffer_methods, obj, name); +} + +static void xu_buffer_dealloc(PyObject *self) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + free(xub->buf); + PyObject_Del(self); +} + +static PyTypeObject xu_buffer_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "buffer", + sizeof(xu_buffer_object), + 0, + xu_buffer_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_buffer_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** MODULE WRAPPER *********************** + */ + +static void handle_child_death(int dummy) +{ + while ( waitpid(-1, NULL, WNOHANG) > 0 ) + continue; +} + +static PyObject *xu_autoreap(PyObject *self, PyObject *args) +{ + struct sigaction sa; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handle_child_death; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; + (void)sigaction(SIGCHLD, &sa, NULL); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef xu_methods[] = { + { "notifier", xu_notifier_new, METH_VARARGS, + "Create a new notifier." }, + { "message", xu_message_new, METH_VARARGS, + "Create a new communications message." }, + { "port", xu_port_new, METH_VARARGS, + "Create a new communications port." }, + { "buffer", xu_buffer_new, METH_VARARGS, + "Create a new ring buffer." }, + { "autoreap", xu_autoreap, METH_VARARGS, + "Ensure that zombie children are automatically reaped by the OS." }, + { NULL, NULL, 0, NULL } +}; + +PyMODINIT_FUNC initutils(void) +{ + PyObject *m, *d; + + m = Py_InitModule("xend.utils", xu_methods); + + d = PyModule_GetDict(m); + port_error = PyErr_NewException("xend.utils.PortError", NULL, NULL); + PyDict_SetItemString(d, "PortError", port_error); +} diff --git a/tools/xend-old/setup.py b/tools/xend-old/setup.py new file mode 100644 index 0000000000..66ed7a937e --- /dev/null +++ b/tools/xend-old/setup.py @@ -0,0 +1,19 @@ + +from distutils.core import setup, Extension + +utils = Extension("utils", + extra_compile_args = ["-fno-strict-aliasing"], + include_dirs = ["../xc/lib", + "../../xen/include/hypervisor-ifs", + "../../linux-xen-sparse/include"], + library_dirs = ["../xc/lib"], + libraries = ["xc"], + sources = ["lib/utils.c"]) + +setup(name = "xend", + version = "1.0", + packages = ["xend"], + package_dir = { "xend" : "lib" }, + ext_package = "xend", + ext_modules = [ utils ] + ) diff --git a/tools/xend-old/xend b/tools/xend-old/xend new file mode 100755 index 0000000000..6e321bbad3 --- /dev/null +++ b/tools/xend-old/xend @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +import os, sys, xend.main + +def main(): + if not sys.argv[1:]: + print 'usage: %s {start|stop|restart}' % sys.argv[0] + elif os.fork(): + pid, status = os.wait() + return status >> 8 + elif sys.argv[1] == 'start': + return xend.main.start_daemon() + elif sys.argv[1] == 'stop': + return xend.main.stop_daemon() + elif sys.argv[1] == 'restart': + return xend.main.stop_daemon() or xend.main.start_daemon() + else: + print 'not an option:', sys.argv[1] + return 1 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/xend/Makefile b/tools/xend/Makefile deleted file mode 100644 index 8e28c0386a..0000000000 --- a/tools/xend/Makefile +++ /dev/null @@ -1,17 +0,0 @@ - -all: - python setup.py build - -install: all - if [ "$(prefix)" = "" ]; then \ - python setup.py install; \ - elif [ "$(dist)" = "yes" ]; then \ - python setup.py install --home="$(prefix)"; \ - else \ - python setup.py install --root="$(prefix)"; \ - fi - mkdir -p $(prefix)/usr/sbin - install -m0755 xend $(prefix)/usr/sbin - -clean: - rm -rf build *.pyc *.pyo *.a *.so *.o *~ *.rpm diff --git a/tools/xend/lib/__init__.py b/tools/xend/lib/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tools/xend/lib/blkif.py b/tools/xend/lib/blkif.py deleted file mode 100644 index 51431b694a..0000000000 --- a/tools/xend/lib/blkif.py +++ /dev/null @@ -1,214 +0,0 @@ - -################################################################# -## xend/blkif.py -- Block-interface management functions for Xend -## Copyright (c) 2004, K A Fraser (University of Cambridge) -################################################################# - -import errno, re, os, select, signal, socket, sys -import xend.main, xend.console, xend.manager, xend.utils, Xc - -CMSG_BLKIF_BE = 1 -CMSG_BLKIF_FE = 2 -CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED = 0 -CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED = 32 -CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED = 32 -CMSG_BLKIF_FE_INTERFACE_CONNECT = 33 -CMSG_BLKIF_FE_INTERFACE_DISCONNECT = 34 -CMSG_BLKIF_BE_CREATE = 0 -CMSG_BLKIF_BE_DESTROY = 1 -CMSG_BLKIF_BE_CONNECT = 2 -CMSG_BLKIF_BE_DISCONNECT = 3 -CMSG_BLKIF_BE_VBD_CREATE = 4 -CMSG_BLKIF_BE_VBD_DESTROY = 5 -CMSG_BLKIF_BE_VBD_GROW = 6 -CMSG_BLKIF_BE_VBD_SHRINK = 7 - -BLKIF_DRIVER_STATUS_DOWN = 0 -BLKIF_DRIVER_STATUS_UP = 1 - -pendmsg = None -pendaddr = None - -recovery = False # Is a recovery in progress? (if so we'll need to notify guests) -be_port = None # Port object for backend domain - -def backend_tx_req(msg): - port = xend.blkif.be_port - if not port: - print "BUG: attempt to transmit request to non-existant blkif driver" - if port.space_to_write_request(): - port.write_request(msg) - port.notify() - else: - xend.blkif.pendmsg = msg - -def backend_rx_req(port, msg): - port.write_response(msg) - subtype = (msg.get_header())['subtype'] - print "Received blkif-be request, subtype %d" % subtype - if subtype == CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: - status = (msg.get_payload())['status'] - if status == BLKIF_DRIVER_STATUS_UP: - if xend.blkif.recovery: - # Nasty hack: we count the number of VBDs we reattach so that - # we'll know when to notify the guests. Must make this better! - interface.rebuilt_so_far = 0 - interface.nr_to_rebuild = 0 - print "New blkif backend now UP, rebuilding VBDs:" - for blkif_key in interface.list.keys(): - blkif = interface.list[blkif_key] - blkif.create() - for vdev in blkif.devices.keys(): - blkif.reattach_device(vdev) - interface.nr_to_rebuild += 1 - else: - print "Unexpected block backend driver status: %d" % status - - -def backend_rx_rsp(port, msg): - subtype = (msg.get_header())['subtype'] - print "Received blkif-be response, subtype %d" % subtype - if subtype == CMSG_BLKIF_BE_CREATE: - rsp = { 'success': True } - xend.main.send_management_response(rsp, xend.blkif.pendaddr) - elif subtype == CMSG_BLKIF_BE_CONNECT: - pl = msg.get_payload() - (dom, hnd, frame, evtchn, st) = (pl['domid'], pl['blkif_handle'], - pl['shmem_frame'], pl['evtchn'], - pl['status']) - blkif = interface.list[xend.main.port_from_dom(dom).local_port] - msg = xend.utils.message(CMSG_BLKIF_FE, - CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED, 0, - { 'handle' : 0, 'status' : 2, - 'evtchn' : blkif.evtchn['port2'] }) - blkif.ctrlif_tx_req(xend.main.port_list[blkif.key], msg) - elif subtype == CMSG_BLKIF_BE_VBD_CREATE: - pl = msg.get_payload() - (dom, hnd, vdev, ro, st) = (pl['domid'], pl['blkif_handle'], - pl['vdevice'], pl['readonly'], - pl['status']) - blkif = interface.list[xend.main.port_from_dom(dom).local_port] - (pdev, start_sect, nr_sect, readonly) = blkif.devices[vdev] - msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW, 0, - { 'domid' : dom, 'blkif_handle' : 0, - 'vdevice' : vdev, - 'extent.sector_start' : start_sect, - 'extent.sector_length' : nr_sect, - 'extent.device' : pdev }) - backend_tx_req(msg) - elif subtype == CMSG_BLKIF_BE_VBD_GROW: - if not xend.blkif.recovery: - rsp = { 'success': True } - xend.main.send_management_response(rsp, xend.blkif.pendaddr) - else: - interface.rebuilt_so_far += 1 - if interface.rebuilt_so_far == interface.nr_to_rebuild: - print "Rebuilt VBDs, notifying guests:" - for blkif_key in interface.list.keys(): - blkif = interface.list[blkif_key] - print " Notifying %d" % blkif.dom - msg = xend.utils.message(CMSG_BLKIF_FE, - CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED, - 0, { 'handle' : 0, 'status' : 1 }) - blkif.ctrlif_tx_req(xend.main.port_from_dom(blkif.dom),msg) - xend.blkif.recovery = False - print "Done notifying guests" - - -def backend_do_work(port): - global pendmsg - if pendmsg and port.space_to_write_request(): - port.write_request(pendmsg) - pendmsg = None - return True - return False - - -class interface: - - # Dictionary of all block-device interfaces. - list = {} - - # NB. 'key' is an opaque value that has no meaning in this class. - def __init__(self, dom, key): - self.dom = dom - self.key = key - self.devices = {} - self.pendmsg = None - interface.list[key] = self - self.create() - - def create(self): - msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE, 0, - { 'domid' : self.dom, 'blkif_handle' : 0 }) - xend.blkif.pendaddr = xend.main.mgmt_req_addr - backend_tx_req(msg) - - # Attach a device to the specified interface - def attach_device(self, vdev, pdev, start_sect, nr_sect, readonly): - if self.devices.has_key(vdev): - return False - self.devices[vdev] = (pdev, start_sect, nr_sect, readonly) - msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE, 0, - { 'domid' : self.dom, 'blkif_handle' : 0, - 'vdevice' : vdev, 'readonly' : readonly }) - xend.blkif.pendaddr = xend.main.mgmt_req_addr - backend_tx_req(msg) - return True - - def reattach_device(self, vdev): - (pdev, start_sect, nr_sect, readonly) = self.devices[vdev] - msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE, - 0, { 'domid' : self.dom, - 'blkif_handle' : 0, - 'vdevice' : vdev, - 'readonly' : readonly }) - xend.blkif.pendaddr = xend.main.mgmt_req_addr - backend_tx_req(msg) - - # Completely destroy this interface. - def destroy(self): - del interface.list[self.key] - msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY, 0, - { 'domid' : self.dom, 'blkif_handle' : 0 }) - backend_tx_req(msg) - - - # The parameter @port is the control-interface event channel. This method - # returns True if messages were written to the control interface. - def ctrlif_transmit_work(self, port): - if self.pendmsg and port.space_to_write_request(): - port.write_request(self.pendmsg) - self.pendmsg = None - return True - return False - - def ctrlif_tx_req(self, port, msg): - if port.space_to_write_request(): - port.write_request(msg) - port.notify() - else: - self.pendmsg = msg - - def ctrlif_rx_req(self, port, msg): - port.write_response(msg) - subtype = (msg.get_header())['subtype'] - if subtype == CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED: - msg = xend.utils.message(CMSG_BLKIF_FE, - CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED, - 0, - { 'handle' : 0, 'status' : 1 }) - self.ctrlif_tx_req(port, msg) - elif subtype == CMSG_BLKIF_FE_INTERFACE_CONNECT: - pl = msg.get_payload() - (hnd, frame) = (pl['handle'], pl['shmem_frame']) - xc = Xc.new() - self.evtchn = xc.evtchn_bind_interdomain( - dom1=xend.blkif.be_port.remote_dom, - dom2=self.dom) - msg = xend.utils.message(CMSG_BLKIF_BE, - CMSG_BLKIF_BE_CONNECT, 0, - { 'domid' : self.dom, 'blkif_handle' : 0, - 'shmem_frame' : frame, - 'evtchn' : self.evtchn['port1'] }) - backend_tx_req(msg) diff --git a/tools/xend/lib/console.py b/tools/xend/lib/console.py deleted file mode 100644 index 57898817f5..0000000000 --- a/tools/xend/lib/console.py +++ /dev/null @@ -1,180 +0,0 @@ - -############################################################# -## xend/console.py -- Console-management functions for Xend -## Copyright (c) 2004, K A Fraser (University of Cambridge) -############################################################# - -import errno, re, os, select, signal, socket, struct, sys -import xend.blkif, xend.main, xend.manager, xend.utils, Xc - -## -## interface: -## Each control interface owns an instance of this class, which manages -## the current state of the console interface. Normally a console interface -## will be one of two state: -## LISTENING: listening for a connection on TCP port 'self.port' -## CONNECTED: sending/receiving console data on TCP port 'self.port' -## -## A dictionary of all active interfaces, indexed by TCP socket descriptor, -## is accessible as 'interface.list_by_fd'. -## -## NB. When a class instance is to be destroyed you *must* call the 'close' -## method. Otherwise a stale reference will eb left in the interface list. -## -class interface: - - # The various states that a console interface may be in. - CLOSED = 0 # No console activity - LISTENING = 1 # Listening on port 'self.port'. Socket object 'self.sock'. - CONNECTED = 2 # Active connection on 'self.port'. Socket obj 'self.sock'. - - - # Dictionary of all active (non-closed) console interfaces. - list_by_fd = {} - - - # Dictionary of all console interfaces, closed and open. - list = {} - - - # NB. 'key' is an opaque value that has no meaning in this class. - def __init__(self, port, key): - self.status = interface.CLOSED - self.port = port - self.key = key - self.rbuf = xend.utils.buffer() - self.wbuf = xend.utils.buffer() - interface.list[key] = self - - - # Is this interface closed (inactive)? - def closed(self): - return self.status == interface.CLOSED - - - # Is this interface listening? - def listening(self): - return self.status == interface.LISTENING - - - # Is this interface active and connected? - def connected(self): - return self.status == interface.CONNECTED - - - # Close the interface, if it is not closed already. - def close(self): - if not self.closed(): - del interface.list_by_fd[self.sock.fileno()] - self.sock.close() - del self.sock - self.status = interface.CLOSED - - - # Move the interface into the 'listening' state. Opens a new listening - # socket and updates 'list_by_fd'. - def listen(self): - # Close old socket (if any), and create a fresh one. - self.close() - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) - - try: - # Turn the new socket into a non-blocking listener. - self.sock.setblocking(False) - self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.sock.bind(('', self.port)) - self.sock.listen(1) - - # Announce the new status of thsi interface. - self.status = interface.LISTENING - interface.list_by_fd[self.sock.fileno()] = self - - except: - # In case of trouble ensure we get rid of dangling socket reference - self.sock.close() - del self.sock - raise - - - # Move a listening interface into the 'connected' state. - def connect(self): - # Pick up a new connection, if one is available. - try: - (sock, addr) = self.sock.accept() - except: - return 0 - sock.setblocking(False) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - - # Close the listening socket. - self.sock.close() - - # Publish the new socket and the new interface state. - self.sock = sock - self.status = interface.CONNECTED - interface.list_by_fd[self.sock.fileno()] = self - return 1 - - - # Completely sestroy a console interface. - def destroy(self): - self.close() - del interface.list[self.key] - - - # Do work triggered by resource availability on a console-interface socket. - def socket_work(self): - # If the interface is listening, check for pending connections. - if self.listening(): - self.connect() - - # All done if the interface is not connected. - if not self.connected(): - return - - # Send as much pending data as possible via the socket. - while not self.rbuf.empty(): - try: - bytes = self.sock.send(self.rbuf.peek()) - if bytes > 0: - self.rbuf.discard(bytes) - except socket.error, error: - pass - - # Read as much data as is available. Don't worry about - # overflowing our buffer: it's more important to read the - # incoming data stream and detect errors or closure of the - # remote end in a timely manner. - try: - while 1: - data = self.sock.recv(2048) - # Return of zero means the remote end has disconnected. - # We therefore return the console interface to listening. - if not data: - self.listen() - break - self.wbuf.write(data) - except socket.error, error: - # Assume that most errors mean that the connection is dead. - # In such cases we return the interface to 'listening' state. - if error[0] != errno.EAGAIN: - print "Better return to listening" - self.listen() - print "New status: " + str(self.status) - - - # The parameter @port is the control-interface event channel. This method - # returns True if messages were written to the control interface. - def ctrlif_transmit_work(self, port): - work_done = False - while not self.wbuf.empty() and port.space_to_write_request(): - msg = xend.utils.message(0, 0, 0) - msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD)) - port.write_request(msg) - work_done = True - return work_done - - - def ctrlif_rx_req(self, port, msg): - self.rbuf.write(msg.get_payload()) - port.write_response(msg) diff --git a/tools/xend/lib/domain_controller.h b/tools/xend/lib/domain_controller.h deleted file mode 100644 index 76dd164fcb..0000000000 --- a/tools/xend/lib/domain_controller.h +++ /dev/null @@ -1,532 +0,0 @@ -/****************************************************************************** - * domain_controller.h - * - * Interface to server controller (e.g., 'xend'). This header file defines the - * interface that is shared with guest OSes. - * - * Copyright (c) 2004, K A Fraser - */ - -#ifndef __DOMAIN_CONTROLLER_H__ -#define __DOMAIN_CONTROLLER_H__ - - -#ifndef BASIC_START_INFO -#error "Xen header file hypervisor-if.h must already be included here." -#endif - - -/* - * EXTENDED BOOTSTRAP STRUCTURE FOR NEW DOMAINS. - */ - -typedef struct { - BASIC_START_INFO; - u16 domain_controller_evtchn; /* 320 */ -} PACKED extended_start_info_t; /* 322 bytes */ -#define SIF_BLK_BE_DOMAIN (1<<4) /* Is this a block backend domain? */ -#define SIF_NET_BE_DOMAIN (1<<5) /* Is this a net backend domain? */ - - -/* - * Reason codes for SCHEDOP_shutdown. These are opaque to Xen but may be - * interpreted by control software to determine the appropriate action. These - * are only really advisories: the controller can actually do as it likes. - */ -#define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */ -#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */ -#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ - - -/* - * CONTROLLER MESSAGING INTERFACE. - */ - -typedef struct { - u8 type; /* 0: echoed in response */ - u8 subtype; /* 1: echoed in response */ - u8 id; /* 2: echoed in response */ - u8 length; /* 3: number of bytes in 'msg' */ - u8 msg[60]; /* 4: type-specific message data */ -} PACKED control_msg_t; /* 64 bytes */ - -#define CONTROL_RING_SIZE 8 -typedef u32 CONTROL_RING_IDX; -#define MASK_CONTROL_IDX(_i) ((_i)&(CONTROL_RING_SIZE-1)) - -typedef struct { - control_msg_t tx_ring[CONTROL_RING_SIZE]; /* 0: guest -> controller */ - control_msg_t rx_ring[CONTROL_RING_SIZE]; /* 512: controller -> guest */ - CONTROL_RING_IDX tx_req_prod, tx_resp_prod; /* 1024, 1028 */ - CONTROL_RING_IDX rx_req_prod, rx_resp_prod; /* 1032, 1036 */ -} PACKED control_if_t; /* 1040 bytes */ - -/* - * Top-level command types. - */ -#define CMSG_CONSOLE 0 /* Console */ -#define CMSG_BLKIF_BE 1 /* Block-device backend */ -#define CMSG_BLKIF_FE 2 /* Block-device frontend */ -#define CMSG_NETIF_BE 3 /* Network-device backend */ -#define CMSG_NETIF_FE 4 /* Network-device frontend */ -#define CMSG_SHUTDOWN 6 /* Shutdown messages */ - - -/****************************************************************************** - * CONSOLE DEFINITIONS - */ - -/* - * Subtypes for console messages. - */ -#define CMSG_CONSOLE_DATA 0 - - -/****************************************************************************** - * BLOCK-INTERFACE FRONTEND DEFINITIONS - */ - -/* Messages from domain controller to guest. */ -#define CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED 0 - -/* Messages from guest to domain controller. */ -#define CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED 32 -#define CMSG_BLKIF_FE_INTERFACE_CONNECT 33 -#define CMSG_BLKIF_FE_INTERFACE_DISCONNECT 34 - -/* These are used by both front-end and back-end drivers. */ -#define blkif_vdev_t u16 -#define blkif_pdev_t u16 -#define blkif_sector_t u64 - -/* - * CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED: - * Notify a guest about a status change on one of its block interfaces. - * If the interface is DESTROYED or DOWN then the interface is disconnected: - * 1. The shared-memory frame is available for reuse. - * 2. Any unacknowledged messgaes pending on the interface were dropped. - */ -#define BLKIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */ -#define BLKIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */ -#define BLKIF_INTERFACE_STATUS_CONNECTED 2 /* Exists and is connected. */ -typedef struct { - u32 handle; /* 0 */ - u32 status; /* 4 */ - u16 evtchn; /* 8: (only if status == BLKIF_INTERFACE_STATUS_CONNECTED). */ -} PACKED blkif_fe_interface_status_changed_t; /* 10 bytes */ - -/* - * CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED: - * Notify the domain controller that the front-end driver is DOWN or UP. - * When the driver goes DOWN then the controller will send no more - * status-change notifications. When the driver comes UP then the controller - * will send a notification for each interface that currently exists. - * If the driver goes DOWN while interfaces are still UP, the domain - * will automatically take the interfaces DOWN. - */ -#define BLKIF_DRIVER_STATUS_DOWN 0 -#define BLKIF_DRIVER_STATUS_UP 1 -typedef struct { - /* IN */ - u32 status; /* 0: BLKIF_DRIVER_STATUS_??? */ - /* OUT */ - /* - * Tells driver how many interfaces it should expect to immediately - * receive notifications about. - */ - u32 nr_interfaces; /* 4 */ -} PACKED blkif_fe_driver_status_changed_t; /* 8 bytes */ - -/* - * CMSG_BLKIF_FE_INTERFACE_CONNECT: - * If successful, the domain controller will acknowledge with a - * STATUS_CONNECTED message. - */ -typedef struct { - u32 handle; /* 0 */ - u32 __pad; - memory_t shmem_frame; /* 8 */ - MEMORY_PADDING; -} PACKED blkif_fe_interface_connect_t; /* 16 bytes */ - -/* - * CMSG_BLKIF_FE_INTERFACE_DISCONNECT: - * If successful, the domain controller will acknowledge with a - * STATUS_DISCONNECTED message. - */ -typedef struct { - u32 handle; /* 0 */ -} PACKED blkif_fe_interface_disconnect_t; /* 4 bytes */ - - -/****************************************************************************** - * BLOCK-INTERFACE BACKEND DEFINITIONS - */ - -/* Messages from domain controller. */ -#define CMSG_BLKIF_BE_CREATE 0 /* Create a new block-device interface. */ -#define CMSG_BLKIF_BE_DESTROY 1 /* Destroy a block-device interface. */ -#define CMSG_BLKIF_BE_CONNECT 2 /* Connect i/f to remote driver. */ -#define CMSG_BLKIF_BE_DISCONNECT 3 /* Disconnect i/f from remote driver. */ -#define CMSG_BLKIF_BE_VBD_CREATE 4 /* Create a new VBD for an interface. */ -#define CMSG_BLKIF_BE_VBD_DESTROY 5 /* Delete a VBD from an interface. */ -#define CMSG_BLKIF_BE_VBD_GROW 6 /* Append an extent to a given VBD. */ -#define CMSG_BLKIF_BE_VBD_SHRINK 7 /* Remove last extent from a given VBD. */ - -/* Messages to domain controller. */ -#define CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED 32 - -/* - * Message request/response definitions for block-device messages. - */ - -typedef struct { - blkif_sector_t sector_start; /* 0 */ - blkif_sector_t sector_length; /* 8 */ - blkif_pdev_t device; /* 16 */ - u16 __pad; /* 18 */ -} PACKED blkif_extent_t; /* 20 bytes */ - -/* Non-specific 'okay' return. */ -#define BLKIF_BE_STATUS_OKAY 0 -/* Non-specific 'error' return. */ -#define BLKIF_BE_STATUS_ERROR 1 -/* The following are specific error returns. */ -#define BLKIF_BE_STATUS_INTERFACE_EXISTS 2 -#define BLKIF_BE_STATUS_INTERFACE_NOT_FOUND 3 -#define BLKIF_BE_STATUS_INTERFACE_CONNECTED 4 -#define BLKIF_BE_STATUS_VBD_EXISTS 5 -#define BLKIF_BE_STATUS_VBD_NOT_FOUND 6 -#define BLKIF_BE_STATUS_OUT_OF_MEMORY 7 -#define BLKIF_BE_STATUS_EXTENT_NOT_FOUND 8 -#define BLKIF_BE_STATUS_MAPPING_ERROR 9 - -/* This macro can be used to create an array of descriptive error strings. */ -#define BLKIF_BE_STATUS_ERRORS { \ - "Okay", \ - "Non-specific error", \ - "Interface already exists", \ - "Interface not found", \ - "Interface is still connected", \ - "VBD already exists", \ - "VBD not found", \ - "Out of memory", \ - "Extent not found for VBD", \ - "Could not map domain memory" } - -/* - * CMSG_BLKIF_BE_CREATE: - * When the driver sends a successful response then the interface is fully - * created. The controller will send a DOWN notification to the front-end - * driver. - */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Domain attached to new interface. */ - u32 blkif_handle; /* 4: Domain-specific interface handle. */ - /* OUT */ - u32 status; /* 8 */ -} PACKED blkif_be_create_t; /* 12 bytes */ - -/* - * CMSG_BLKIF_BE_DESTROY: - * When the driver sends a successful response then the interface is fully - * torn down. The controller will send a DESTROYED notification to the - * front-end driver. - */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Identify interface to be destroyed. */ - u32 blkif_handle; /* 4: ...ditto... */ - /* OUT */ - u32 status; /* 8 */ -} PACKED blkif_be_destroy_t; /* 12 bytes */ - -/* - * CMSG_BLKIF_BE_CONNECT: - * When the driver sends a successful response then the interface is fully - * connected. The controller will send a CONNECTED notification to the - * front-end driver. - */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Domain attached to new interface. */ - u32 blkif_handle; /* 4: Domain-specific interface handle. */ - memory_t shmem_frame; /* 8: Page cont. shared comms window. */ - MEMORY_PADDING; - u32 evtchn; /* 16: Event channel for notifications. */ - /* OUT */ - u32 status; /* 20 */ -} PACKED blkif_be_connect_t; /* 24 bytes */ - -/* - * CMSG_BLKIF_BE_DISCONNECT: - * When the driver sends a successful response then the interface is fully - * disconnected. The controller will send a DOWN notification to the front-end - * driver. - */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Domain attached to new interface. */ - u32 blkif_handle; /* 4: Domain-specific interface handle. */ - /* OUT */ - u32 status; /* 8 */ -} PACKED blkif_be_disconnect_t; /* 12 bytes */ - -/* CMSG_BLKIF_BE_VBD_CREATE */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Identify blkdev interface. */ - u32 blkif_handle; /* 4: ...ditto... */ - blkif_vdev_t vdevice; /* 8: Interface-specific id for this VBD. */ - u16 readonly; /* 10: Non-zero -> VBD isn't writeable. */ - /* OUT */ - u32 status; /* 12 */ -} PACKED blkif_be_vbd_create_t; /* 16 bytes */ - -/* CMSG_BLKIF_BE_VBD_DESTROY */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Identify blkdev interface. */ - u32 blkif_handle; /* 4: ...ditto... */ - blkif_vdev_t vdevice; /* 8: Interface-specific id of the VBD. */ - u16 __pad; /* 10 */ - /* OUT */ - u32 status; /* 12 */ -} PACKED blkif_be_vbd_destroy_t; /* 16 bytes */ - -/* CMSG_BLKIF_BE_VBD_GROW */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Identify blkdev interface. */ - u32 blkif_handle; /* 4: ...ditto... */ - blkif_extent_t extent; /* 8: Physical extent to append to VBD. */ - blkif_vdev_t vdevice; /* 28: Interface-specific id of the VBD. */ - u16 __pad; /* 30 */ - /* OUT */ - u32 status; /* 32 */ -} PACKED blkif_be_vbd_grow_t; /* 36 bytes */ - -/* CMSG_BLKIF_BE_VBD_SHRINK */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Identify blkdev interface. */ - u32 blkif_handle; /* 4: ...ditto... */ - blkif_vdev_t vdevice; /* 8: Interface-specific id of the VBD. */ - u16 __pad; /* 10 */ - /* OUT */ - u32 status; /* 12 */ -} PACKED blkif_be_vbd_shrink_t; /* 16 bytes */ - -/* - * CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: - * Notify the domain controller that the back-end driver is DOWN or UP. - * If the driver goes DOWN while interfaces are still UP, the controller - * will automatically send DOWN notifications. - */ -typedef struct { - u32 status; /* 0: BLKIF_DRIVER_STATUS_??? */ -} PACKED blkif_be_driver_status_changed_t; /* 4 bytes */ - - -/****************************************************************************** - * NETWORK-INTERFACE FRONTEND DEFINITIONS - */ - -/* Messages from domain controller to guest. */ -#define CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED 0 - -/* Messages from guest to domain controller. */ -#define CMSG_NETIF_FE_DRIVER_STATUS_CHANGED 32 -#define CMSG_NETIF_FE_INTERFACE_CONNECT 33 -#define CMSG_NETIF_FE_INTERFACE_DISCONNECT 34 - -/* - * CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED: - * Notify a guest about a status change on one of its network interfaces. - * If the interface is DESTROYED or DOWN then the interface is disconnected: - * 1. The shared-memory frame is available for reuse. - * 2. Any unacknowledged messgaes pending on the interface were dropped. - */ -#define NETIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */ -#define NETIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */ -#define NETIF_INTERFACE_STATUS_CONNECTED 2 /* Exists and is connected. */ -typedef struct { - u32 handle; /* 0 */ - u32 status; /* 4 */ - u16 evtchn; /* 8: status == NETIF_INTERFACE_STATUS_CONNECTED */ - u8 mac[6]; /* 10: status == NETIF_INTERFACE_STATUS_CONNECTED */ -} PACKED netif_fe_interface_status_changed_t; /* 16 bytes */ - -/* - * CMSG_NETIF_FE_DRIVER_STATUS_CHANGED: - * Notify the domain controller that the front-end driver is DOWN or UP. - * When the driver goes DOWN then the controller will send no more - * status-change notifications. When the driver comes UP then the controller - * will send a notification for each interface that currently exists. - * If the driver goes DOWN while interfaces are still UP, the domain - * will automatically take the interfaces DOWN. - */ -#define NETIF_DRIVER_STATUS_DOWN 0 -#define NETIF_DRIVER_STATUS_UP 1 -typedef struct { - /* IN */ - u32 status; /* 0: NETIF_DRIVER_STATUS_??? */ - /* OUT */ - /* - * Tells driver how many interfaces it should expect to immediately - * receive notifications about. - */ - u32 nr_interfaces; /* 4 */ -} PACKED netif_fe_driver_status_changed_t; /* 8 bytes */ - -/* - * CMSG_NETIF_FE_INTERFACE_CONNECT: - * If successful, the domain controller will acknowledge with a - * STATUS_CONNECTED message. - */ -typedef struct { - u32 handle; /* 0 */ - u32 __pad; /* 4 */ - memory_t tx_shmem_frame; /* 8 */ - MEMORY_PADDING; - memory_t rx_shmem_frame; /* 16 */ - MEMORY_PADDING; -} PACKED netif_fe_interface_connect_t; /* 24 bytes */ - -/* - * CMSG_NETIF_FE_INTERFACE_DISCONNECT: - * If successful, the domain controller will acknowledge with a - * STATUS_DISCONNECTED message. - */ -typedef struct { - u32 handle; /* 0 */ -} PACKED netif_fe_interface_disconnect_t; /* 4 bytes */ - - -/****************************************************************************** - * NETWORK-INTERFACE BACKEND DEFINITIONS - */ - -/* Messages from domain controller. */ -#define CMSG_NETIF_BE_CREATE 0 /* Create a new net-device interface. */ -#define CMSG_NETIF_BE_DESTROY 1 /* Destroy a net-device interface. */ -#define CMSG_NETIF_BE_CONNECT 2 /* Connect i/f to remote driver. */ -#define CMSG_NETIF_BE_DISCONNECT 3 /* Disconnect i/f from remote driver. */ - -/* Messages to domain controller. */ -#define CMSG_NETIF_BE_DRIVER_STATUS_CHANGED 32 - -/* - * Message request/response definitions for net-device messages. - */ - -/* Non-specific 'okay' return. */ -#define NETIF_BE_STATUS_OKAY 0 -/* Non-specific 'error' return. */ -#define NETIF_BE_STATUS_ERROR 1 -/* The following are specific error returns. */ -#define NETIF_BE_STATUS_INTERFACE_EXISTS 2 -#define NETIF_BE_STATUS_INTERFACE_NOT_FOUND 3 -#define NETIF_BE_STATUS_INTERFACE_CONNECTED 4 -#define NETIF_BE_STATUS_OUT_OF_MEMORY 5 -#define NETIF_BE_STATUS_MAPPING_ERROR 6 - -/* This macro can be used to create an array of descriptive error strings. */ -#define NETIF_BE_STATUS_ERRORS { \ - "Okay", \ - "Non-specific error", \ - "Interface already exists", \ - "Interface not found", \ - "Interface is still connected", \ - "Out of memory", \ - "Could not map domain memory" } - -/* - * CMSG_NETIF_BE_CREATE: - * When the driver sends a successful response then the interface is fully - * created. The controller will send a DOWN notification to the front-end - * driver. - */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Domain attached to new interface. */ - u32 netif_handle; /* 4: Domain-specific interface handle. */ - u8 mac[6]; /* 8 */ - u16 __pad; /* 14 */ - /* OUT */ - u32 status; /* 16 */ -} PACKED netif_be_create_t; /* 20 bytes */ - -/* - * CMSG_NETIF_BE_DESTROY: - * When the driver sends a successful response then the interface is fully - * torn down. The controller will send a DESTROYED notification to the - * front-end driver. - */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Identify interface to be destroyed. */ - u32 netif_handle; /* 4: ...ditto... */ - /* OUT */ - u32 status; /* 8 */ -} PACKED netif_be_destroy_t; /* 12 bytes */ - -/* - * CMSG_NETIF_BE_CONNECT: - * When the driver sends a successful response then the interface is fully - * connected. The controller will send a CONNECTED notification to the - * front-end driver. - */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Domain attached to new interface. */ - u32 netif_handle; /* 4: Domain-specific interface handle. */ - memory_t tx_shmem_frame; /* 8: Page cont. tx shared comms window. */ - MEMORY_PADDING; - memory_t rx_shmem_frame; /* 16: Page cont. rx shared comms window. */ - MEMORY_PADDING; - u16 evtchn; /* 24: Event channel for notifications. */ - u16 __pad; /* 26 */ - /* OUT */ - u32 status; /* 28 */ -} PACKED netif_be_connect_t; /* 32 bytes */ - -/* - * CMSG_NETIF_BE_DISCONNECT: - * When the driver sends a successful response then the interface is fully - * disconnected. The controller will send a DOWN notification to the front-end - * driver. - */ -typedef struct { - /* IN */ - domid_t domid; /* 0: Domain attached to new interface. */ - u32 netif_handle; /* 4: Domain-specific interface handle. */ - /* OUT */ - u32 status; /* 8 */ -} PACKED netif_be_disconnect_t; /* 12 bytes */ - -/* - * CMSG_NETIF_BE_DRIVER_STATUS_CHANGED: - * Notify the domain controller that the back-end driver is DOWN or UP. - * If the driver goes DOWN while interfaces are still UP, the domain - * will automatically send DOWN notifications. - */ -typedef struct { - u32 status; /* 0: NETIF_DRIVER_STATUS_??? */ -} PACKED netif_be_driver_status_changed_t; /* 4 bytes */ - - -/****************************************************************************** - * SHUTDOWN DEFINITIONS - */ - -/* - * Subtypes for shutdown messages. - */ -#define CMSG_SHUTDOWN_POWEROFF 0 /* Clean shutdown (SHUTDOWN_poweroff). */ -#define CMSG_SHUTDOWN_REBOOT 1 /* Clean shutdown (SHUTDOWN_reboot). */ -#define CMSG_SHUTDOWN_SUSPEND 2 /* Create suspend info, then */ - /* SHUTDOWN_suspend. */ - -#endif /* __DOMAIN_CONTROLLER_H__ */ diff --git a/tools/xend/lib/main.py b/tools/xend/lib/main.py deleted file mode 100755 index 0eecd17a05..0000000000 --- a/tools/xend/lib/main.py +++ /dev/null @@ -1,317 +0,0 @@ - -########################################################### -## xend.py -- Xen controller daemon -## Copyright (c) 2004, K A Fraser (University of Cambridge) -########################################################### - -import errno, re, os, pwd, select, signal, socket, struct, sys, time -import xend.blkif, xend.netif, xend.console, xend.manager, xend.utils, Xc - - -# The following parameters could be placed in a configuration file. -PID = '/var/run/xend.pid' -LOG = '/var/log/xend.log' -USER = 'root' -CONTROL_DIR = '/var/run/xend' -UNIX_SOCK = 'management_sock' # relative to CONTROL_DIR - - -CMSG_CONSOLE = 0 -CMSG_BLKIF_BE = 1 -CMSG_BLKIF_FE = 2 -CMSG_NETIF_BE = 3 -CMSG_NETIF_FE = 4 - - -def port_from_dom(dom): - global port_list - for idx, port in port_list.items(): - if port.remote_dom == dom: - return port - return None - - -def send_management_response(response, addr): - try: - response = str(response) - print "Mgmt_rsp[%s]: %s" % (addr, response) - management_interface.sendto(response, addr) - except socket.error, error: - pass - - -def daemon_loop(): - # Could we do this more nicely? The xend.manager functions need access - # to this global state to do their work. - global port_list, notifier, management_interface, mgmt_req_addr, dom0_port - - # Lists of all interfaces, indexed by local event-channel port. - port_list = {} - - xc = Xc.new() - - # Ignore writes to disconnected sockets. We clean up differently. - signal.signal(signal.SIGPIPE, signal.SIG_IGN) - - # Construct the management interface. This is a UNIX domain socket via - # which we receive 'request' datagrams. Each request is a string that - # can be eval'ed as a Python statement. Responses can be remotely eval'ed - # by the requester to create a Python dictionary of result values. - management_interface = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0) - if os.path.exists(CONTROL_DIR+'/'+UNIX_SOCK): - os.unlink(CONTROL_DIR+'/'+UNIX_SOCK) - management_interface.setblocking(False) - management_interface.bind(CONTROL_DIR+'/'+UNIX_SOCK) - - # Interface via which we receive event notifications from other guest - # OSes. This interface also allows us to clear/acknowledge outstanding - # notifications. - notifier = xend.utils.notifier() - - # The DOM0 control interface is not set up via the management interface. - # Note that console messages don't come our way (actually, only driver - # back-ends should use the DOM0 control interface). - dom0_port = xend.utils.port(0) - xend.netif.be_port = dom0_port - xend.blkif.be_port = dom0_port - notifier.bind(dom0_port.local_port) - port_list[dom0_port.local_port] = dom0_port - - ## - ## MAIN LOOP - ## - while 1: - - # Construct a poll set. We wait on: - # 1. Requests on the management interface. - # 2. Incoming event-channel notifications. - # Furthermore, for each active control interface: - # 3. Incoming console data. - # 4. Space for outgoing console data (if there is data to send). - waitset = select.poll() - waitset.register(management_interface, select.POLLIN) - waitset.register(notifier, select.POLLIN) - for idx, con_if in xend.console.interface.list_by_fd.items(): - if not con_if.closed(): - pflags = select.POLLIN - if not con_if.rbuf.empty() and con_if.connected(): - pflags = select.POLLIN | select.POLLOUT - waitset.register(con_if.sock.fileno(), pflags) - - # Wait for something to do... - fdset = waitset.poll() - - # Look for messages on the management interface. - # These should consist of executable Python statements that call - # well-known management functions (e.g., new_control_interface(dom=9)). - try: - data, mgmt_req_addr = management_interface.recvfrom(2048) - except socket.error, error: - if error[0] != errno.EAGAIN: - raise - else: - if mgmt_req_addr: - # Evaluate the request in an exception-trapping sandbox. - try: - print "Mgmt_req[%s]: %s" % (mgmt_req_addr, data) - response = eval('xend.manager.'+data) - - except: - # Catch all exceptions and turn into an error response: - # status: False - # error_type: 'exception' - # exception_type: name of exception type. - # exception value: textual exception value. - exc_type, exc_val = sys.exc_info()[:2] - response = { 'success': False } - response['error_type'] = 'exception' - response['exception_type'] = str(exc_type) - response['exception_value'] = str(exc_val) - response = str(response) - - # Try to send a response to the requester. - if response: - send_management_response(response, mgmt_req_addr) - - # Do work for every console interface that hit in the poll set. - for (fd, events) in fdset: - if xend.console.interface.list_by_fd.has_key(fd): - con_if = xend.console.interface.list_by_fd[fd] - con_if.socket_work() - # We may now have pending data to send via the control - # interface. If so then send all we can and notify the remote. - port = port_list[con_if.key] - if con_if.ctrlif_transmit_work(port): - port.notify() - - # Process control-interface notifications from other guest OSes. - while 1: - # Grab a notification, if there is one. - notification = notifier.read() - if not notification: - break - (idx, type) = notification - - if not port_list.has_key(idx): - continue - - port = port_list[idx] - work_done = False - - con_if = False - if xend.console.interface.list.has_key(idx): - con_if = xend.console.interface.list[idx] - - blk_if = False - if xend.blkif.interface.list.has_key(idx): - blk_if = xend.blkif.interface.list[idx] - - net_if = False - if xend.netif.interface.list.has_key(idx): - net_if = xend.netif.interface.list[idx] - - # If we pick up a disconnect notification then we do any necessary - # cleanup. - if type == notifier.EXCEPTION: - ret = xc.evtchn_status(idx) - if ret['status'] == 'unbound': - notifier.unbind(idx) - del port_list[idx], port - if con_if: - con_if.destroy() - del con_if - if blk_if: - blk_if.destroy() - del blk_if - if net_if: - net_if.destroy() - del net_if - continue - - # Process incoming requests. - while port.request_to_read(): - msg = port.read_request() - work_done = True - type = (msg.get_header())['type'] - if type == CMSG_CONSOLE and con_if: - con_if.ctrlif_rx_req(port, msg) - elif type == CMSG_BLKIF_FE and blk_if: - blk_if.ctrlif_rx_req(port, msg) - elif type == CMSG_BLKIF_BE and port == xend.blkif.be_port: - xend.blkif.backend_rx_req(port, msg) - elif type == CMSG_NETIF_FE and net_if: - net_if.ctrlif_rx_req(port, msg) - elif type == CMSG_NETIF_BE and port == xend.netif.be_port: - xend.netif.backend_rx_req(port, msg) - else: - port.write_response(msg) - - # Process incoming responses. - while port.response_to_read(): - msg = port.read_response() - work_done = True - type = (msg.get_header())['type'] - if type == CMSG_BLKIF_BE and port == xend.blkif.be_port: - xend.blkif.backend_rx_rsp(port, msg) - elif type == CMSG_NETIF_BE and port == xend.netif.be_port: - xend.netif.backend_rx_rsp(port, msg) - - # Send console data. - if con_if and con_if.ctrlif_transmit_work(port): - work_done = True - - # Send blkif messages. - if blk_if and blk_if.ctrlif_transmit_work(port): - work_done = True - - # Send netif messages. - if net_if and net_if.ctrlif_transmit_work(port): - work_done = True - - # Back-end block-device work. - if port == dom0_port and xend.blkif.backend_do_work(port): - work_done = True - - # Back-end network-device work. - if port == xend.netif.be_port and xend.netif.backend_do_work(port): - work_done = True - - # Finally, notify the remote end of any work that we did. - if work_done: - port.notify() - - # Unmask notifications for this port. - notifier.unmask(idx) - - - -def cleanup_daemon(kill=False): - # No cleanup to do if the PID file is empty. - if not os.path.isfile(PID) or not os.path.getsize(PID): - return 0 - # Read the PID of the previous invocation and search active process list. - pid = open(PID, 'r').read() - lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines() - for line in lines: - if re.search('^ *' + pid + '.+xend', line): - if not kill: - print "Daemon is already running (PID %d)" % int(pid) - return 1 - # Old daemon is still active: terminate it. - os.kill(int(pid), 1) - # Delete the, now stale, PID file. - os.remove(PID) - return 0 - - - -def start_daemon(): - if cleanup_daemon(kill=False): - return 1 - - if not os.path.exists(CONTROL_DIR): - os.mkdir(CONTROL_DIR) - - # Open log file. Truncate it if non-empty, and request line buffering. - if os.path.isfile(LOG): - os.rename(LOG, LOG+'.old') - logfile = open(LOG, 'w+', 1) - - # Detach from TTY. - os.setsid() - - # Set the UID. - try: - os.setuid(pwd.getpwnam(USER)[2]) - except KeyError, error: - print "Error: no such user '%s'" % USER - return 1 - - # Ensure that zombie children are automatically reaped. - xend.utils.autoreap() - - # Fork -- parent writes the PID file and exits. - pid = os.fork() - if pid: - pidfile = open(PID, 'w') - pidfile.write(str(pid)) - pidfile.close() - return 0 - - # Close down standard file handles - try: - os.close(0) # stdin - os.close(1) # stdout - os.close(2) # stderr - except: - pass - - # Redirect output to log file, then enter the main loop. - sys.stdout = sys.stderr = logfile - daemon_loop() - return 0 - - - -def stop_daemon(): - return cleanup_daemon(kill=True) diff --git a/tools/xend/lib/manager.py b/tools/xend/lib/manager.py deleted file mode 100644 index 49517583b5..0000000000 --- a/tools/xend/lib/manager.py +++ /dev/null @@ -1,174 +0,0 @@ - -############################################################# -## xend/manager.py -- Management-interface functions for Xend -## Copyright (c) 2004, K A Fraser (University of Cambridge) -############################################################# - -import xend.blkif, xend.netif, xend.console, xend.main, xend.utils - - -## -## new_control_interface: -## Create a new control interface with the specified domain @dom. -## The console port may also be specified; otherwise a suitable port is -## automatically allocated. -## -def new_control_interface(dom, console_port=-1): - # Allocate an event channel and binbd to it. - port = xend.utils.port(dom) - xend.main.notifier.bind(port.local_port) - - # If necessary, compute a suitable TCP port for console I/O. - if console_port < 0: - console_port = 9600 + port.local_port - - # Create a listening console interface. - con_if = xend.console.interface(console_port, port.local_port) - con_if.listen() - - # Update the master port list. - xend.main.port_list[port.local_port] = port - - # Construct the successful response to be returned to the requester. - response = { 'success': True } - response['local_port'] = port.local_port - response['remote_port'] = port.remote_port - response['console_port'] = console_port - return response - - -## -## new_block_interface: -## Create a new block interface for the specified domain @dom. -## -def new_block_interface(dom, handle=-1): - # By default we create an interface with handle zero. - if handle < 0: - handle = 0 - - # We only support one interface per domain, which must have handle zero. - if handle != 0: - response = { 'success': False } - response['error_type'] = 'Bad handle %d (only handle 0 ' + \ - 'is supported)' % handle - return response - - # Find local event-channel port associated with the specified domain. - port = xend.main.port_from_dom(dom) - if not port: - response = { 'success': False } - response['error_type'] = 'Unknown domain %d' % dom - return response - - # The interface must not already exist. - if xend.blkif.interface.list.has_key(port.local_port): - response = { 'success': False } - response['error_type'] = 'Interface (dom=%d,handle=%d) already ' + \ - 'exists' % (dom, handle) - return response - - # Create the new interface. Initially no virtual devices are attached. - xend.blkif.interface(dom, port.local_port) - - # Response is deferred until back-end driver sends acknowledgement. - return None - - -## -## new_block_device: -## Attach a new virtual block device to the specified block interface -## (@dom, @handle). The new device is identified by @vdev, and maps to -## the real block extent (@pdev, @start_sect, @nr_sect). If @readonly then -## write requests to @vdev will be rejected. -## -def new_block_device(dom, handle, vdev, pdev, start_sect, nr_sect, readonly): - # We only support one interface per domain, which must have handle zero. - if handle != 0: - response = { 'success': False } - response['error_type'] = 'Bad handle %d (only handle 0 ' + \ - 'is supported)' % handle - return response - - # Find local event-channel port associated with the specified domain. - port = xend.main.port_from_dom(dom) - if not port: - response = { 'success': False } - response['error_type'] = 'Unknown domain %d' % dom - return response - - # The interface must exist. - if not xend.blkif.interface.list.has_key(port.local_port): - response = { 'success': False } - response['error_type'] = 'Interface (dom=%d,handle=%d) does not ' + \ - 'exists' % (dom, handle) - return response - - # The virtual device must not yet exist. - blkif = xend.blkif.interface.list[port.local_port] - if not blkif.attach_device(vdev, pdev, start_sect, nr_sect, readonly): - response = { 'success': False } - response['error_type'] = 'Vdevice (dom=%d,handle=%d,vdevice=%d) ' + \ - 'already exists' % (dom, handle, vdev) - return response - - # Response is deferred until back-end driver sends acknowledgement. - return None - - -## -## new_network_interface: -## Create a new network interface for the specified domain @dom. -## -def new_network_interface(dom, handle=-1): - # By default we create an interface with handle zero. - if handle < 0: - handle = 0 - - # We only support one interface per domain, which must have handle zero. - if handle != 0: - response = { 'success': False } - response['error_type'] = 'Bad handle %d (only handle 0 ' + \ - 'is supported)' % handle - return response - - # Find local event-channel port associated with the specified domain. - port = xend.main.port_from_dom(dom) - if not port: - response = { 'success': False } - response['error_type'] = 'Unknown domain %d' % dom - return response - - # The interface must not already exist. - if xend.netif.interface.list.has_key(port.local_port): - response = { 'success': False } - response['error_type'] = 'Interface (dom=%d,handle=%d) already ' + \ - 'exists' % (dom, handle) - return response - - # Create the new interface. Initially no virtual devices are attached. - xend.netif.interface(dom, port.local_port) - - # Response is deferred until back-end driver sends acknowledgement. - return None - -## -## set_network_backend -## Authorise a domain to act as the net backend (assumes we only have one -## backend driver for now). After this call, back end "up" notifications -## for the network will only be accepted from this domain. -## -def set_network_backend(dom): - if xend.netif.be_port.remote_dom != 0: - xend.netif.recovery = True - xend.netif.be_port = xend.main.port_from_dom(dom) - return { 'success' : True } - -## -## set_block_backend -## Authorise a domain to act as the block backend (assumes we only have one -## backend driver for now). After this call, back end "up" notifications -## for the network will only be accepted from this domain. -def set_block_backend(dom): - if xend.blkif.be_port: xend.blkif.recovery = True - xend.blkif.be_port = xend.main.port_from_dom(dom) - return { 'success' : True } diff --git a/tools/xend/lib/netif.py b/tools/xend/lib/netif.py deleted file mode 100644 index eaa10086a2..0000000000 --- a/tools/xend/lib/netif.py +++ /dev/null @@ -1,187 +0,0 @@ - -################################################################### -## xend/netif.py -- Network-interface management functions for Xend -## Copyright (c) 2004, K A Fraser (University of Cambridge) -################################################################### - -import errno, random, re, os, select, signal, socket, struct, sys -import xend.main, xend.console, xend.manager, xend.utils, Xc - -CMSG_NETIF_BE = 3 -CMSG_NETIF_FE = 4 -CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED = 0 -CMSG_NETIF_FE_DRIVER_STATUS_CHANGED = 32 -CMSG_NETIF_BE_DRIVER_STATUS_CHANGED = 32 -CMSG_NETIF_FE_INTERFACE_CONNECT = 33 -CMSG_NETIF_FE_INTERFACE_DISCONNECT = 34 -CMSG_NETIF_BE_CREATE = 0 -CMSG_NETIF_BE_DESTROY = 1 -CMSG_NETIF_BE_CONNECT = 2 -CMSG_NETIF_BE_DISCONNECT = 3 - -NETIF_DRIVER_STATUS_DOWN = 0 -NETIF_DRIVER_STATUS_UP = 1 - -pendmsg = None -pendaddr = None - -recovery = False # Is a recovery in progress? -be_port = None # Port object for backend domain - -def backend_tx_req(msg): - if xend.netif.be_port.space_to_write_request(): - xend.netif.be_port.write_request(msg) - xend.netif.be_port.notify() - else: - xend.netif.pendmsg = msg - -def backend_rx_req(port, msg): - port.write_response(msg) - subtype = (msg.get_header())['subtype'] - print "Received netif-be request, subtype %d" % subtype - if subtype == CMSG_NETIF_BE_DRIVER_STATUS_CHANGED: - pl = msg.get_payload() - status = pl['status'] - if status == NETIF_DRIVER_STATUS_UP: - if xend.netif.recovery: - print "New netif backend now UP, notifying guests:" - for netif_key in interface.list.keys(): - netif = interface.list[netif_key] - netif.create() - print " Notifying %d" % netif.dom - msg = xend.utils.message( - CMSG_NETIF_FE, - CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0, - { 'handle' : 0, 'status' : 1 }) - netif.ctrlif_tx_req(xend.main.port_from_dom(netif.dom),msg) - print "Done notifying guests" - recovery = False - else: - print "Unexpected net backend driver status: %d" % status - -def backend_rx_rsp(port, msg): - subtype = (msg.get_header())['subtype'] - print "Received netif-be response, subtype %d" % subtype - if subtype == CMSG_NETIF_BE_CREATE: - rsp = { 'success': True } - xend.main.send_management_response(rsp, xend.netif.pendaddr) - elif subtype == CMSG_NETIF_BE_CONNECT: - pl = msg.get_payload() - (dom, hnd, evtchn, tx_frame, rx_frame, st) = ( - pl['domid'], pl['netif_handle'], pl['evtchn'], - pl['tx_shmem_frame'], pl['rx_shmem_frame'], pl['status']) - netif = interface.list[xend.main.port_from_dom(dom).local_port] - msg = xend.utils.message(CMSG_NETIF_FE, - CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0, - { 'handle' : 0, 'status' : 2, - 'evtchn' : netif.evtchn['port2'], - 'mac[0]' : netif.mac[0], - 'mac[1]' : netif.mac[1], - 'mac[2]' : netif.mac[2], - 'mac[3]' : netif.mac[3], - 'mac[4]' : netif.mac[4], - 'mac[5]' : netif.mac[5] }) - netif.ctrlif_tx_req(xend.main.port_list[netif.key], msg) - -def backend_do_work(port): - global pendmsg - if pendmsg and port.space_to_write_request(): - port.write_request(pendmsg) - pendmsg = None - return True - return False - - -class interface: - - # Dictionary of all network-device interfaces. - list = {} - - drvdom = None - - # NB. 'key' is an opaque value that has no meaning in this class. - def __init__(self, dom, key): - self.dom = dom - self.key = key - self.pendmsg = None - - # VIFs get a random MAC address with a "special" vendor id. - # - # NB. The vendor is currently an "obsolete" one that used to belong - # to DEC (AA-00-00). Using it is probably a bit rude :-) - # - # NB2. The first bit of the first random octet is set to zero for - # all dynamic MAC addresses. This may allow us to manually specify - # MAC addresses for some VIFs with no fear of clashes. - self.mac = [ 0xaa, 0x00, 0x00 ] - self.mac.append(int(random.random()*128)) - self.mac.append(int(random.random()*256)) - self.mac.append(int(random.random()*256)) - - interface.list[key] = self - self.create() - - def create(self): - """Notify the current network back end to create the virtual interface - represented by this object.""" - msg = xend.utils.message(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE, 0, - { 'domid' : self.dom, 'netif_handle' : 0, - 'mac[0]' : self.mac[0], - 'mac[1]' : self.mac[1], - 'mac[2]' : self.mac[2], - 'mac[3]' : self.mac[3], - 'mac[4]' : self.mac[4], - 'mac[5]' : self.mac[5] }) - xend.netif.pendaddr = xend.main.mgmt_req_addr - backend_tx_req(msg) - - - # Completely destroy this interface. - def destroy(self): - del interface.list[self.key] - msg = xend.utils.message(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY, 0, - { 'domid' : self.dom, 'netif_handle' : 0 }) - backend_tx_req(msg) - - - # The parameter @port is the control-interface event channel. This method - # returns True if messages were written to the control interface. - def ctrlif_transmit_work(self, port): - if self.pendmsg and port.space_to_write_request(): - port.write_request(self.pendmsg) - self.pendmsg = None - return True - return False - - def ctrlif_tx_req(self, port, msg): - if port.space_to_write_request(): - port.write_request(msg) - port.notify() - else: - self.pendmsg = msg - - def ctrlif_rx_req(self, port, msg): - port.write_response(msg) - subtype = (msg.get_header())['subtype'] - if subtype == CMSG_NETIF_FE_DRIVER_STATUS_CHANGED: - print "netif driver up message from %d" % port.remote_dom - msg = xend.utils.message(CMSG_NETIF_FE, - CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0, - { 'handle' : 0, 'status' : 1 }) - self.ctrlif_tx_req(port, msg) - elif subtype == CMSG_NETIF_FE_INTERFACE_CONNECT: - print "netif connect request from %d" % port.remote_dom - pl = msg.get_payload() - (hnd, tx_frame, rx_frame) = (pl['handle'], pl['tx_shmem_frame'], - pl['rx_shmem_frame']) - xc = Xc.new() - self.evtchn = xc.evtchn_bind_interdomain( - dom1=xend.netif.be_port.remote_dom, - dom2=self.dom) - msg = xend.utils.message(CMSG_NETIF_BE, - CMSG_NETIF_BE_CONNECT, 0, - { 'domid' : self.dom, 'netif_handle' : 0, - 'tx_shmem_frame' : tx_frame, - 'rx_shmem_frame' : rx_frame, - 'evtchn' : self.evtchn['port1'] }) - backend_tx_req(msg) diff --git a/tools/xend/lib/utils.c b/tools/xend/lib/utils.c deleted file mode 100644 index 2d96577203..0000000000 --- a/tools/xend/lib/utils.c +++ /dev/null @@ -1,1384 +0,0 @@ -/****************************************************************************** - * utils.c - * - * Copyright (c) 2004, K A Fraser - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "domain_controller.h" - -#include - -/* Needed for Python versions earlier than 2.3. */ -#ifndef PyMODINIT_FUNC -#define PyMODINIT_FUNC DL_EXPORT(void) -#endif - -/* NB. The following should be kept in sync with the kernel's evtchn driver. */ -#define EVTCHN_DEV_NAME "/dev/xen/evtchn" -#define EVTCHN_DEV_MAJOR 10 -#define EVTCHN_DEV_MINOR 200 -#define PORT_NORMAL 0x0000 /* A standard event notification. */ -#define PORT_EXCEPTION 0x8000 /* An exceptional notification. */ -#define PORTIDX_MASK 0x7fff /* Strip subtype to obtain port index. */ -/* /dev/xen/evtchn ioctls: */ -/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */ -#define EVTCHN_RESET _IO('E', 1) -/* EVTCHN_BIND: Bind to teh specified event-channel port. */ -#define EVTCHN_BIND _IO('E', 2) -/* EVTCHN_UNBIND: Unbind from the specified event-channel port. */ -#define EVTCHN_UNBIND _IO('E', 3) - -/* Size of a machine page frame. */ -#define PAGE_SIZE 4096 - - -/* - * *********************** NOTIFIER *********************** - */ - -typedef struct { - PyObject_HEAD; - int evtchn_fd; -} xu_notifier_object; - -static PyObject *xu_notifier_read(PyObject *self, PyObject *args) -{ - xu_notifier_object *xun = (xu_notifier_object *)self; - u16 v; - int bytes; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - while ( (bytes = read(xun->evtchn_fd, &v, sizeof(v))) == -1 ) - { - if ( errno == EINTR ) - continue; - if ( errno == EAGAIN ) - goto none; - return PyErr_SetFromErrno(PyExc_IOError); - } - - if ( bytes == sizeof(v) ) - return Py_BuildValue("(i,i)", v&PORTIDX_MASK, v&~PORTIDX_MASK); - - none: - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *xu_notifier_unmask(PyObject *self, PyObject *args) -{ - xu_notifier_object *xun = (xu_notifier_object *)self; - u16 v; - int idx; - - if ( !PyArg_ParseTuple(args, "i", &idx) ) - return NULL; - - v = (u16)idx; - - (void)write(xun->evtchn_fd, &v, sizeof(v)); - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *xu_notifier_bind(PyObject *self, PyObject *args) -{ - xu_notifier_object *xun = (xu_notifier_object *)self; - int idx; - - if ( !PyArg_ParseTuple(args, "i", &idx) ) - return NULL; - - if ( ioctl(xun->evtchn_fd, EVTCHN_BIND, idx) != 0 ) - return PyErr_SetFromErrno(PyExc_IOError); - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *xu_notifier_unbind(PyObject *self, PyObject *args) -{ - xu_notifier_object *xun = (xu_notifier_object *)self; - int idx; - - if ( !PyArg_ParseTuple(args, "i", &idx) ) - return NULL; - - if ( ioctl(xun->evtchn_fd, EVTCHN_UNBIND, idx) != 0 ) - return PyErr_SetFromErrno(PyExc_IOError); - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args) -{ - xu_notifier_object *xun = (xu_notifier_object *)self; - return PyInt_FromLong(xun->evtchn_fd); -} - -static PyMethodDef xu_notifier_methods[] = { - { "read", - (PyCFunction)xu_notifier_read, - METH_VARARGS, - "Read a (@port, @type) pair.\n" }, - - { "unmask", - (PyCFunction)xu_notifier_unmask, - METH_VARARGS, - "Unmask notifications for a @port.\n" }, - - { "bind", - (PyCFunction)xu_notifier_bind, - METH_VARARGS, - "Get notifications for a @port.\n" }, - - { "unbind", - (PyCFunction)xu_notifier_unbind, - METH_VARARGS, - "No longer get notifications for a @port.\n" }, - - { "fileno", - (PyCFunction)xu_notifier_fileno, - METH_VARARGS, - "Return the file descriptor for the notification channel.\n" }, - - { NULL, NULL, 0, NULL } -}; - -staticforward PyTypeObject xu_notifier_type; - -static PyObject *xu_notifier_new(PyObject *self, PyObject *args) -{ - xu_notifier_object *xun; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - xun = PyObject_New(xu_notifier_object, &xu_notifier_type); - - reopen: - xun->evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR); - if ( xun->evtchn_fd == -1 ) - { - if ( (errno == ENOENT) && - ((mkdir("/dev/xen", 0755) == 0) || (errno == EEXIST)) && - (mknod(EVTCHN_DEV_NAME, S_IFCHR|0600, - (EVTCHN_DEV_MAJOR << 8) | EVTCHN_DEV_MINOR) == 0) ) - goto reopen; - PyObject_Del((PyObject *)xun); - return PyErr_SetFromErrno(PyExc_IOError); - } - - return (PyObject *)xun; -} - -static PyObject *xu_notifier_getattr(PyObject *obj, char *name) -{ - if ( strcmp(name, "EXCEPTION") == 0 ) - return PyInt_FromLong(PORT_EXCEPTION); - if ( strcmp(name, "NORMAL") == 0 ) - return PyInt_FromLong(PORT_NORMAL); - return Py_FindMethod(xu_notifier_methods, obj, name); -} - -static void xu_notifier_dealloc(PyObject *self) -{ - xu_notifier_object *xun = (xu_notifier_object *)self; - (void)close(xun->evtchn_fd); - PyObject_Del(self); -} - -static PyTypeObject xu_notifier_type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, - "notifier", - sizeof(xu_notifier_object), - 0, - xu_notifier_dealloc, /* tp_dealloc */ - NULL, /* tp_print */ - xu_notifier_getattr, /* tp_getattr */ - NULL, /* tp_setattr */ - NULL, /* tp_compare */ - NULL, /* tp_repr */ - NULL, /* tp_as_number */ - NULL, /* tp_as_sequence */ - NULL, /* tp_as_mapping */ - NULL /* tp_hash */ -}; - - - -/* - * *********************** MESSAGE *********************** - */ - -#define TYPE(_x,_y) (((_x)<<8)|(_y)) -#define P2C(_struct, _field, _ctype) \ - do { \ - PyObject *obj; \ - if ( (obj = PyDict_GetItemString(payload, #_field)) != NULL ) \ - { \ - if ( PyInt_Check(obj) ) \ - { \ - ((_struct *)&xum->msg.msg[0])->_field = \ - (_ctype)PyInt_AsLong(obj); \ - dict_items_parsed++; \ - } \ - else if ( PyLong_Check(obj) ) \ - { \ - ((_struct *)&xum->msg.msg[0])->_field = \ - (_ctype)PyLong_AsUnsignedLongLong(obj); \ - dict_items_parsed++; \ - } \ - } \ - xum->msg.length = sizeof(_struct); \ - } while ( 0 ) -#define C2P(_struct, _field, _pytype, _ctype) \ - do { \ - PyObject *obj = Py ## _pytype ## _From ## _ctype \ - (((_struct *)&xum->msg.msg[0])->_field); \ - if ( dict == NULL ) dict = PyDict_New(); \ - PyDict_SetItemString(dict, #_field, obj); \ - } while ( 0 ) - -typedef struct { - PyObject_HEAD; - control_msg_t msg; -} xu_message_object; - -static PyObject *xu_message_append_payload(PyObject *self, PyObject *args) -{ - xu_message_object *xum = (xu_message_object *)self; - char *str; - int len; - - if ( !PyArg_ParseTuple(args, "s#", &str, &len) ) - return NULL; - - if ( (len + xum->msg.length) > sizeof(xum->msg.msg) ) - { - PyErr_SetString(PyExc_RuntimeError, "out of space in control message"); - return NULL; - } - - memcpy(&xum->msg.msg[xum->msg.length], str, len); - xum->msg.length += len; - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *xu_message_set_response_fields(PyObject *self, PyObject *args) -{ - xu_message_object *xum = (xu_message_object *)self; - PyObject *payload; - int dict_items_parsed = 0; - - if ( !PyArg_ParseTuple(args, "O", &payload) ) - return NULL; - - if ( !PyDict_Check(payload) ) - { - PyErr_SetString(PyExc_TypeError, "payload is not a dictionary"); - return NULL; - } - - switch ( TYPE(xum->msg.type, xum->msg.subtype) ) - { - case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED): - P2C(blkif_fe_driver_status_changed_t, nr_interfaces, u32); - break; - case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): - P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32); - break; - } - - if ( dict_items_parsed != PyDict_Size(payload) ) - { - PyErr_SetString(PyExc_TypeError, "payload contains bad items"); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *xu_message_get_payload(PyObject *self, PyObject *args) -{ - xu_message_object *xum = (xu_message_object *)self; - PyObject *dict = NULL; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - switch ( TYPE(xum->msg.type, xum->msg.subtype) ) - { - case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED): - C2P(blkif_fe_interface_status_changed_t, handle, Int, Long); - C2P(blkif_fe_interface_status_changed_t, status, Int, Long); - C2P(blkif_fe_interface_status_changed_t, evtchn, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED): - C2P(blkif_fe_driver_status_changed_t, status, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT): - C2P(blkif_fe_interface_connect_t, handle, Int, Long); - C2P(blkif_fe_interface_connect_t, shmem_frame, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_DISCONNECT): - C2P(blkif_fe_interface_disconnect_t, handle, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE): - C2P(blkif_be_create_t, domid, Int, Long); - C2P(blkif_be_create_t, blkif_handle, Int, Long); - C2P(blkif_be_create_t, status, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY): - C2P(blkif_be_destroy_t, domid, Int, Long); - C2P(blkif_be_destroy_t, blkif_handle, Int, Long); - C2P(blkif_be_destroy_t, status, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT): - C2P(blkif_be_connect_t, domid, Int, Long); - C2P(blkif_be_connect_t, blkif_handle, Int, Long); - C2P(blkif_be_connect_t, shmem_frame, Int, Long); - C2P(blkif_be_connect_t, evtchn, Int, Long); - C2P(blkif_be_connect_t, status, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT): - C2P(blkif_be_disconnect_t, domid, Int, Long); - C2P(blkif_be_disconnect_t, blkif_handle, Int, Long); - C2P(blkif_be_disconnect_t, status, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE): - C2P(blkif_be_vbd_create_t, domid, Int, Long); - C2P(blkif_be_vbd_create_t, blkif_handle, Int, Long); - C2P(blkif_be_vbd_create_t, vdevice, Int, Long); - C2P(blkif_be_vbd_create_t, readonly, Int, Long); - C2P(blkif_be_vbd_create_t, status, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY): - C2P(blkif_be_vbd_destroy_t, domid, Int, Long); - C2P(blkif_be_vbd_destroy_t, blkif_handle, Int, Long); - C2P(blkif_be_vbd_destroy_t, vdevice, Int, Long); - C2P(blkif_be_vbd_destroy_t, status, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW): - C2P(blkif_be_vbd_grow_t, domid, Int, Long); - C2P(blkif_be_vbd_grow_t, blkif_handle, Int, Long); - C2P(blkif_be_vbd_grow_t, vdevice, Int, Long); - C2P(blkif_be_vbd_grow_t, extent.sector_start, - Long, UnsignedLongLong); - C2P(blkif_be_vbd_grow_t, extent.sector_length, - Long, UnsignedLongLong); - C2P(blkif_be_vbd_grow_t, extent.device, Int, Long); - C2P(blkif_be_vbd_grow_t, status, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_SHRINK): - C2P(blkif_be_vbd_shrink_t, domid, Int, Long); - C2P(blkif_be_vbd_shrink_t, blkif_handle, Int, Long); - C2P(blkif_be_vbd_shrink_t, vdevice, Int, Long); - C2P(blkif_be_vbd_shrink_t, status, Int, Long); - return dict; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED): - C2P(blkif_be_driver_status_changed_t, status, Int, Long); - return dict; - case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED): - C2P(netif_fe_interface_status_changed_t, handle, Int, Long); - C2P(netif_fe_interface_status_changed_t, status, Int, Long); - C2P(netif_fe_interface_status_changed_t, evtchn, Int, Long); - return dict; - case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): - C2P(netif_fe_driver_status_changed_t, status, Int, Long); - C2P(netif_fe_driver_status_changed_t, nr_interfaces, Int, Long); - return dict; - case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT): - C2P(netif_fe_interface_connect_t, handle, Int, Long); - C2P(netif_fe_interface_connect_t, tx_shmem_frame, Int, Long); - C2P(netif_fe_interface_connect_t, rx_shmem_frame, Int, Long); - return dict; - case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_DISCONNECT): - C2P(netif_fe_interface_disconnect_t, handle, Int, Long); - return dict; - case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE): - C2P(netif_be_create_t, domid, Int, Long); - C2P(netif_be_create_t, netif_handle, Int, Long); - C2P(netif_be_create_t, status, Int, Long); - return dict; - case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY): - C2P(netif_be_destroy_t, domid, Int, Long); - C2P(netif_be_destroy_t, netif_handle, Int, Long); - C2P(netif_be_destroy_t, status, Int, Long); - return dict; - case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT): - C2P(netif_be_connect_t, domid, Int, Long); - C2P(netif_be_connect_t, netif_handle, Int, Long); - C2P(netif_be_connect_t, tx_shmem_frame, Int, Long); - C2P(netif_be_connect_t, rx_shmem_frame, Int, Long); - C2P(netif_be_connect_t, evtchn, Int, Long); - C2P(netif_be_connect_t, status, Int, Long); - return dict; - case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT): - C2P(netif_be_disconnect_t, domid, Int, Long); - C2P(netif_be_disconnect_t, netif_handle, Int, Long); - C2P(netif_be_disconnect_t, status, Int, Long); - return dict; - case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED): - C2P(netif_be_driver_status_changed_t, status, Int, Long); - return dict; - } - - return PyString_FromStringAndSize(xum->msg.msg, xum->msg.length); -} - -static PyObject *xu_message_get_header(PyObject *self, PyObject *args) -{ - xu_message_object *xum = (xu_message_object *)self; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - return Py_BuildValue("{s:i,s:i,s:i}", - "type", xum->msg.type, - "subtype", xum->msg.subtype, - "id", xum->msg.id); -} - -static PyMethodDef xu_message_methods[] = { - { "append_payload", - (PyCFunction)xu_message_append_payload, - METH_VARARGS, - "Append @str to the message payload.\n" }, - - { "set_response_fields", - (PyCFunction)xu_message_set_response_fields, - METH_VARARGS, - "Fill in the response fields in a message that was passed to us.\n" }, - - { "get_payload", - (PyCFunction)xu_message_get_payload, - METH_VARARGS, - "Return the message payload in string form.\n" }, - - { "get_header", - (PyCFunction)xu_message_get_header, - METH_VARARGS, - "Returns a dictionary of values for @type, @subtype, and @id.\n" }, - - { NULL, NULL, 0, NULL } -}; - -staticforward PyTypeObject xu_message_type; - -static PyObject *xu_message_new(PyObject *self, PyObject *args) -{ - xu_message_object *xum; - int type, subtype, id, dict_items_parsed = 0; - PyObject *payload = NULL; - - if ( !PyArg_ParseTuple(args, "iii|O", &type, &subtype, &id, &payload) ) - return NULL; - - xum = PyObject_New(xu_message_object, &xu_message_type); - - xum->msg.type = type; - xum->msg.subtype = subtype; - xum->msg.id = id; - xum->msg.length = 0; - - if ( payload == NULL ) - return (PyObject *)xum; - - if ( !PyDict_Check(payload) ) - { - PyErr_SetString(PyExc_TypeError, "payload is not a dictionary"); - PyObject_Del((PyObject *)xum); - return NULL; - } - - switch ( TYPE(type, subtype) ) - { - case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED): - P2C(blkif_fe_interface_status_changed_t, handle, u32); - P2C(blkif_fe_interface_status_changed_t, status, u32); - P2C(blkif_fe_interface_status_changed_t, evtchn, u16); - break; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE): - P2C(blkif_be_create_t, domid, u32); - P2C(blkif_be_create_t, blkif_handle, u32); - break; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY): - P2C(blkif_be_destroy_t, domid, u32); - P2C(blkif_be_destroy_t, blkif_handle, u32); - break; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT): - P2C(blkif_be_connect_t, domid, u32); - P2C(blkif_be_connect_t, blkif_handle, u32); - P2C(blkif_be_connect_t, shmem_frame, memory_t); - P2C(blkif_be_connect_t, evtchn, u16); - break; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT): - P2C(blkif_be_disconnect_t, domid, u32); - P2C(blkif_be_disconnect_t, blkif_handle, u32); - break; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE): - P2C(blkif_be_vbd_create_t, domid, u32); - P2C(blkif_be_vbd_create_t, blkif_handle, u32); - P2C(blkif_be_vbd_create_t, vdevice, blkif_vdev_t); - P2C(blkif_be_vbd_create_t, readonly, u16); - break; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY): - P2C(blkif_be_vbd_destroy_t, domid, u32); - P2C(blkif_be_vbd_destroy_t, blkif_handle, u32); - P2C(blkif_be_vbd_destroy_t, vdevice, blkif_vdev_t); - break; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW): - P2C(blkif_be_vbd_grow_t, domid, u32); - P2C(blkif_be_vbd_grow_t, blkif_handle, u32); - P2C(blkif_be_vbd_grow_t, vdevice, blkif_vdev_t); - P2C(blkif_be_vbd_grow_t, extent.sector_start, blkif_sector_t); - P2C(blkif_be_vbd_grow_t, extent.sector_length, blkif_sector_t); - P2C(blkif_be_vbd_grow_t, extent.device, blkif_pdev_t); - break; - case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_SHRINK): - P2C(blkif_be_vbd_shrink_t, domid, u32); - P2C(blkif_be_vbd_shrink_t, blkif_handle, u32); - P2C(blkif_be_vbd_shrink_t, vdevice, blkif_vdev_t); - break; - case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED): - P2C(netif_fe_interface_status_changed_t, handle, u32); - P2C(netif_fe_interface_status_changed_t, status, u32); - P2C(netif_fe_interface_status_changed_t, evtchn, u16); - P2C(netif_fe_interface_status_changed_t, mac[0], u8); - P2C(netif_fe_interface_status_changed_t, mac[1], u8); - P2C(netif_fe_interface_status_changed_t, mac[2], u8); - P2C(netif_fe_interface_status_changed_t, mac[3], u8); - P2C(netif_fe_interface_status_changed_t, mac[4], u8); - P2C(netif_fe_interface_status_changed_t, mac[5], u8); - break; - case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE): - P2C(netif_be_create_t, domid, u32); - P2C(netif_be_create_t, netif_handle, u32); - P2C(netif_be_create_t, mac[0], u8); - P2C(netif_be_create_t, mac[1], u8); - P2C(netif_be_create_t, mac[2], u8); - P2C(netif_be_create_t, mac[3], u8); - P2C(netif_be_create_t, mac[4], u8); - P2C(netif_be_create_t, mac[5], u8); - break; - case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY): - P2C(netif_be_destroy_t, domid, u32); - P2C(netif_be_destroy_t, netif_handle, u32); - break; - case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT): - P2C(netif_be_connect_t, domid, u32); - P2C(netif_be_connect_t, netif_handle, u32); - P2C(netif_be_connect_t, tx_shmem_frame, memory_t); - P2C(netif_be_connect_t, rx_shmem_frame, memory_t); - P2C(netif_be_connect_t, evtchn, u16); - break; - case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT): - P2C(netif_be_disconnect_t, domid, u32); - P2C(netif_be_disconnect_t, netif_handle, u32); - break; - case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): - P2C(netif_fe_driver_status_changed_t, status, u32); - P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32); - break; - } - - if ( dict_items_parsed != PyDict_Size(payload) ) - { - PyErr_SetString(PyExc_TypeError, "payload contains bad items"); - PyObject_Del((PyObject *)xum); - return NULL; - } - - return (PyObject *)xum; -} - -static PyObject *xu_message_getattr(PyObject *obj, char *name) -{ - xu_message_object *xum; - if ( strcmp(name, "MAX_PAYLOAD") == 0 ) - return PyInt_FromLong(sizeof(xum->msg.msg)); - return Py_FindMethod(xu_message_methods, obj, name); -} - -static void xu_message_dealloc(PyObject *self) -{ - PyObject_Del(self); -} - -static PyTypeObject xu_message_type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, - "message", - sizeof(xu_message_object), - 0, - xu_message_dealloc, /* tp_dealloc */ - NULL, /* tp_print */ - xu_message_getattr, /* tp_getattr */ - NULL, /* tp_setattr */ - NULL, /* tp_compare */ - NULL, /* tp_repr */ - NULL, /* tp_as_number */ - NULL, /* tp_as_sequence */ - NULL, /* tp_as_mapping */ - NULL /* tp_hash */ -}; - - - -/* - * *********************** PORT *********************** - */ - -static control_if_t *map_control_interface(int fd, unsigned long pfn) -{ - char *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, - MAP_SHARED, fd, pfn * PAGE_SIZE); - if ( vaddr == MAP_FAILED ) - return NULL; - return (control_if_t *)(vaddr + 2048); -} -static void unmap_control_interface(int fd, control_if_t *c) -{ - char *vaddr = (char *)c - 2048; - (void)munmap(vaddr, PAGE_SIZE); -} - -typedef struct xu_port_object { - PyObject_HEAD; - int mem_fd; - int xc_handle; - u32 remote_dom; - int local_port, remote_port; - control_if_t *interface; - CONTROL_RING_IDX tx_req_cons, tx_resp_prod; - CONTROL_RING_IDX rx_req_prod, rx_resp_cons; -} xu_port_object; - -static PyObject *port_error; - -static int xup_connect(xu_port_object *xup, domid_t dom, - int local_port, int remote_port){ - // From our prespective rx = producer, tx = consumer. - int err = 0; - printf("%s> dom=%u %d:%d\n", __FUNCTION__, (unsigned int)dom, - local_port, remote_port); - - // Consumer = tx. - //xup->interface->tx_resp_prod = 0; - //xup->interface->tx_req_prod = 0; - xup->tx_resp_prod = xup->interface->tx_resp_prod; - xup->tx_req_cons = xup->interface->tx_resp_prod; - printf("%s> tx: %u %u : %u %u\n", __FUNCTION__, - (unsigned int)xup->interface->tx_resp_prod, - (unsigned int)xup->tx_resp_prod, - (unsigned int)xup->tx_req_cons, - (unsigned int)xup->interface->tx_req_prod); - - // Producer = rx. - //xup->interface->rx_req_prod = 0; - //xup->interface->rx_resp_prod = 0; - xup->rx_req_prod = xup->interface->rx_req_prod; - xup->rx_resp_cons = xup->interface->rx_resp_prod; - printf("%s> rx: %u %u : %u %u\n", __FUNCTION__, - (unsigned int)xup->rx_resp_cons, - (unsigned int)xup->interface->rx_resp_prod, - (unsigned int)xup->interface->rx_req_prod, - (unsigned int)xup->rx_req_prod); - - xup->remote_dom = dom; - xup->local_port = local_port; - xup->remote_port = remote_port; - - printf("%s< err=%d\n", __FUNCTION__, err); - return err; -} - -static PyObject *xu_port_notify(PyObject *self, PyObject *args) -{ - xu_port_object *xup = (xu_port_object *)self; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - (void)xc_evtchn_send(xup->xc_handle, xup->local_port); - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *xu_port_read_request(PyObject *self, PyObject *args) -{ - xu_port_object *xup = (xu_port_object *)self; - xu_message_object *xum; - CONTROL_RING_IDX c = xup->tx_req_cons; - control_if_t *cif = xup->interface; - control_msg_t *cmsg; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - if ( (c == cif->tx_req_prod) || - ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) ) - { - PyErr_SetString(port_error, "no request to read"); - return NULL; - } - - cmsg = &cif->tx_ring[MASK_CONTROL_IDX(c)]; - xum = PyObject_New(xu_message_object, &xu_message_type); - memcpy(&xum->msg, cmsg, sizeof(*cmsg)); - if ( xum->msg.length > sizeof(xum->msg.msg) ) - xum->msg.length = sizeof(xum->msg.msg); - xup->tx_req_cons++; - return (PyObject *)xum; -} - -static PyObject *xu_port_write_request(PyObject *self, PyObject *args) -{ - xu_port_object *xup = (xu_port_object *)self; - xu_message_object *xum; - CONTROL_RING_IDX p = xup->rx_req_prod; - control_if_t *cif = xup->interface; - control_msg_t *cmsg; - - if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) ) - return NULL; - - if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) ) - { - PyErr_SetString(PyExc_TypeError, "expected a xend.utils.message"); - return NULL; - } - - if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) ) - { - PyErr_SetString(port_error, "no space to write request"); - return NULL; - } - - cmsg = &cif->rx_ring[MASK_CONTROL_IDX(p)]; - memcpy(cmsg, &xum->msg, sizeof(*cmsg)); - - xup->rx_req_prod = cif->rx_req_prod = p + 1; - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *xu_port_read_response(PyObject *self, PyObject *args) -{ - xu_port_object *xup = (xu_port_object *)self; - xu_message_object *xum; - CONTROL_RING_IDX c = xup->rx_resp_cons; - control_if_t *cif = xup->interface; - control_msg_t *cmsg; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) ) - { - PyErr_SetString(port_error, "no response to read"); - return NULL; - } - - cmsg = &cif->rx_ring[MASK_CONTROL_IDX(c)]; - xum = PyObject_New(xu_message_object, &xu_message_type); - memcpy(&xum->msg, cmsg, sizeof(*cmsg)); - if ( xum->msg.length > sizeof(xum->msg.msg) ) - xum->msg.length = sizeof(xum->msg.msg); - xup->rx_resp_cons++; - return (PyObject *)xum; -} - -static PyObject *xu_port_write_response(PyObject *self, PyObject *args) -{ - xu_port_object *xup = (xu_port_object *)self; - xu_message_object *xum; - CONTROL_RING_IDX p = xup->tx_resp_prod; - control_if_t *cif = xup->interface; - control_msg_t *cmsg; - - if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) ) - return NULL; - - if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) ) - { - PyErr_SetString(PyExc_TypeError, "expected a xend.utils.message"); - return NULL; - } - - if ( p == xup->tx_req_cons ) - { - PyErr_SetString(port_error, "no space to write response"); - return NULL; - } - - cmsg = &cif->tx_ring[MASK_CONTROL_IDX(p)]; - memcpy(cmsg, &xum->msg, sizeof(*cmsg)); - - xup->tx_resp_prod = cif->tx_resp_prod = p + 1; - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *xu_port_request_to_read(PyObject *self, PyObject *args) -{ - xu_port_object *xup = (xu_port_object *)self; - CONTROL_RING_IDX c = xup->tx_req_cons; - control_if_t *cif = xup->interface; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - if ( (c == cif->tx_req_prod) || - ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) ) - return PyInt_FromLong(0); - - return PyInt_FromLong(1); -} - -static PyObject *xu_port_space_to_write_request(PyObject *self, PyObject *args) -{ - xu_port_object *xup = (xu_port_object *)self; - CONTROL_RING_IDX p = xup->rx_req_prod; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) ) - return PyInt_FromLong(0); - - return PyInt_FromLong(1); -} - -static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args) -{ - xu_port_object *xup = (xu_port_object *)self; - CONTROL_RING_IDX c = xup->rx_resp_cons; - control_if_t *cif = xup->interface; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) ) - return PyInt_FromLong(0); - - return PyInt_FromLong(1); -} - -static PyObject *xu_port_space_to_write_response( - PyObject *self, PyObject *args) -{ - xu_port_object *xup = (xu_port_object *)self; - CONTROL_RING_IDX p = xup->tx_resp_prod; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - if ( p == xup->tx_req_cons ) - return PyInt_FromLong(0); - - return PyInt_FromLong(1); -} - -static PyMethodDef xu_port_methods[] = { - { "notify", - (PyCFunction)xu_port_notify, - METH_VARARGS, - "Send a notification to the remote end.\n" }, - - { "read_request", - (PyCFunction)xu_port_read_request, - METH_VARARGS, - "Read a request message from the control interface.\n" }, - - { "write_request", - (PyCFunction)xu_port_write_request, - METH_VARARGS, - "Write a request message to the control interface.\n" }, - - { "read_response", - (PyCFunction)xu_port_read_response, - METH_VARARGS, - "Read a response message from the control interface.\n" }, - - { "write_response", - (PyCFunction)xu_port_write_response, - METH_VARARGS, - "Write a response message to the control interface.\n" }, - - { "request_to_read", - (PyCFunction)xu_port_request_to_read, - METH_VARARGS, - "Returns TRUE if there is a request message to read.\n" }, - - { "space_to_write_request", - (PyCFunction)xu_port_space_to_write_request, - METH_VARARGS, - "Returns TRUE if there is space to write a request message.\n" }, - - { "response_to_read", - (PyCFunction)xu_port_response_to_read, - METH_VARARGS, - "Returns TRUE if there is a response message to read.\n" }, - - { "space_to_write_response", - (PyCFunction)xu_port_space_to_write_response, - METH_VARARGS, - "Returns TRUE if there is space to write a response message.\n" }, - - { NULL, NULL, 0, NULL } -}; - -staticforward PyTypeObject xu_port_type; - -static PyObject *xu_port_new(PyObject *self, PyObject *args) -{ - xu_port_object *xup; - u32 dom; - int port1, port2; - xc_dominfo_t info; - - if ( !PyArg_ParseTuple(args, "i", &dom) ) - return NULL; - - xup = PyObject_New(xu_port_object, &xu_port_type); - - if ( (xup->mem_fd = open("/dev/mem", O_RDWR)) == -1 ) - { - PyErr_SetString(port_error, "Could not open '/dev/mem'"); - goto fail1; - } - - /* Set the General-Purpose Subject whose page frame will be mapped. */ - (void)ioctl(xup->mem_fd, _IO('M', 1), (unsigned long)dom); - - if ( (xup->xc_handle = xc_interface_open()) == -1 ) - { - PyErr_SetString(port_error, "Could not open Xen control interface"); - goto fail2; - } - - if ( dom == 0 ) - { - /* - * The control-interface event channel for DOM0 is already set up. - * We use an ioctl to discover the port at our end of the channel. - */ - port1 = ioctl(xup->xc_handle, IOCTL_PRIVCMD_INITDOMAIN_EVTCHN, NULL); - port2 = -1; /* We don't need the remote end of the DOM0 link. */ - if ( port1 < 0 ) - { - PyErr_SetString(port_error, "Could not open channel to DOM0"); - goto fail3; - } - } - else if ( xc_evtchn_bind_interdomain(xup->xc_handle, - DOMID_SELF, dom, - &port1, &port2) != 0 ) - { - PyErr_SetString(port_error, "Could not open channel to domain"); - goto fail3; - } - - if ( (xc_domain_getinfo(xup->xc_handle, dom, 1, &info) != 1) || - (info.domid != dom) ) - { - PyErr_SetString(port_error, "Failed to obtain domain status"); - goto fail4; - } - - xup->interface = - map_control_interface(xup->mem_fd, info.shared_info_frame); - if ( xup->interface == NULL ) - { - PyErr_SetString(port_error, "Failed to map domain control interface"); - goto fail4; - } - - xup_connect(xup, dom, port1, port2); - return (PyObject *)xup; - - - fail4: - (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, port1); - fail3: - (void)xc_interface_close(xup->xc_handle); - fail2: - (void)close(xup->mem_fd); - fail1: - PyObject_Del((PyObject *)xup); - return NULL; -} - -static PyObject *xu_port_getattr(PyObject *obj, char *name) -{ - xu_port_object *xup = (xu_port_object *)obj; - if ( strcmp(name, "local_port") == 0 ) - return PyInt_FromLong(xup->local_port); - if ( strcmp(name, "remote_port") == 0 ) - return PyInt_FromLong(xup->remote_port); - if ( strcmp(name, "remote_dom") == 0 ) - return PyInt_FromLong(xup->remote_dom); - return Py_FindMethod(xu_port_methods, obj, name); -} - -static void xu_port_dealloc(PyObject *self) -{ - xu_port_object *xup = (xu_port_object *)self; - unmap_control_interface(xup->mem_fd, xup->interface); - if ( xup->remote_dom != 0 ) - (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, xup->local_port); - (void)xc_interface_close(xup->xc_handle); - (void)close(xup->mem_fd); - PyObject_Del(self); -} - -static PyTypeObject xu_port_type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, - "port", - sizeof(xu_port_object), - 0, - xu_port_dealloc, /* tp_dealloc */ - NULL, /* tp_print */ - xu_port_getattr, /* tp_getattr */ - NULL, /* tp_setattr */ - NULL, /* tp_compare */ - NULL, /* tp_repr */ - NULL, /* tp_as_number */ - NULL, /* tp_as_sequence */ - NULL, /* tp_as_mapping */ - NULL /* tp_hash */ -}; - - - -/* - * *********************** BUFFER *********************** - */ - -#define BUFSZ 65536 -#define MASK_BUF_IDX(_i) ((_i)&(BUFSZ-1)) -typedef unsigned int BUF_IDX; - -typedef struct { - PyObject_HEAD; - char *buf; - unsigned int prod, cons; -} xu_buffer_object; - -static PyObject *__xu_buffer_peek(xu_buffer_object *xub, int max) -{ - PyObject *str1, *str2; - int len1, len2, c = MASK_BUF_IDX(xub->cons); - - len1 = xub->prod - xub->cons; - if ( len1 > (BUFSZ - c) ) /* clip to ring wrap */ - len1 = BUFSZ - c; - if ( len1 > max ) /* clip to specified maximum */ - len1 = max; - if ( len1 < 0 ) /* sanity */ - len1 = 0; - - if ( (str1 = PyString_FromStringAndSize(&xub->buf[c], len1)) == NULL ) - return NULL; - - if ( (len1 < (xub->prod - xub->cons)) && (len1 < max) ) - { - len2 = max - len1; - if ( len2 > MASK_BUF_IDX(xub->prod) ) - len2 = MASK_BUF_IDX(xub->prod); - if ( len2 > 0 ) - { - str2 = PyString_FromStringAndSize(&xub->buf[0], len2); - if ( str2 == NULL ) - return NULL; - PyString_ConcatAndDel(&str1, str2); - if ( str1 == NULL ) - return NULL; - } - } - - return str1; -} - -static PyObject *xu_buffer_peek(PyObject *self, PyObject *args) -{ - xu_buffer_object *xub = (xu_buffer_object *)self; - int max = 1024; - - if ( !PyArg_ParseTuple(args, "|i", &max) ) - return NULL; - - return __xu_buffer_peek(xub, max); -} - -static PyObject *xu_buffer_read(PyObject *self, PyObject *args) -{ - xu_buffer_object *xub = (xu_buffer_object *)self; - PyObject *str; - int max = 1024; - - if ( !PyArg_ParseTuple(args, "|i", &max) ) - return NULL; - - if ( (str = __xu_buffer_peek(xub, max)) != NULL ) - xub->cons += PyString_Size(str); - - return str; -} - -static PyObject *xu_buffer_discard(PyObject *self, PyObject *args) -{ - xu_buffer_object *xub = (xu_buffer_object *)self; - int max, len; - - if ( !PyArg_ParseTuple(args, "i", &max) ) - return NULL; - - len = xub->prod - xub->cons; - if ( len > max ) - len = max; - if ( len < 0 ) - len = 0; - - xub->cons += len; - - return PyInt_FromLong(len); -} - -static PyObject *xu_buffer_write(PyObject *self, PyObject *args) -{ - xu_buffer_object *xub = (xu_buffer_object *)self; - char *str; - int len, len1, len2; - - if ( !PyArg_ParseTuple(args, "s#", &str, &len) ) - return NULL; - - len1 = len; - if ( len1 > (BUFSZ - MASK_BUF_IDX(xub->prod)) ) - len1 = BUFSZ - MASK_BUF_IDX(xub->prod); - if ( len1 > (BUFSZ - (xub->prod - xub->cons)) ) - len1 = BUFSZ - (xub->prod - xub->cons); - - if ( len1 == 0 ) - return PyInt_FromLong(0); - - memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[0], len1); - xub->prod += len1; - - if ( len1 < len ) - { - len2 = len - len1; - if ( len2 > (BUFSZ - MASK_BUF_IDX(xub->prod)) ) - len2 = BUFSZ - MASK_BUF_IDX(xub->prod); - if ( len2 > (BUFSZ - (xub->prod - xub->cons)) ) - len2 = BUFSZ - (xub->prod - xub->cons); - if ( len2 != 0 ) - { - memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[len1], len2); - xub->prod += len2; - return PyInt_FromLong(len1 + len2); - } - } - - return PyInt_FromLong(len1); -} - -static PyObject *xu_buffer_empty(PyObject *self, PyObject *args) -{ - xu_buffer_object *xub = (xu_buffer_object *)self; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - if ( xub->cons == xub->prod ) - return PyInt_FromLong(1); - - return PyInt_FromLong(0); -} - -static PyObject *xu_buffer_full(PyObject *self, PyObject *args) -{ - xu_buffer_object *xub = (xu_buffer_object *)self; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - if ( (xub->prod - xub->cons) == BUFSZ ) - return PyInt_FromLong(1); - - return PyInt_FromLong(0); -} - -static PyMethodDef xu_buffer_methods[] = { - { "peek", - (PyCFunction)xu_buffer_peek, - METH_VARARGS, - "Peek up to @max bytes from the buffer. Returns a string.\n" }, - - { "read", - (PyCFunction)xu_buffer_read, - METH_VARARGS, - "Read up to @max bytes from the buffer. Returns a string.\n" }, - - { "discard", - (PyCFunction)xu_buffer_discard, - METH_VARARGS, - "Discard up to @max bytes from the buffer. Returns number of bytes.\n" }, - - { "write", - (PyCFunction)xu_buffer_write, - METH_VARARGS, - "Write @string into buffer. Return number of bytes written.\n" }, - - { "empty", - (PyCFunction)xu_buffer_empty, - METH_VARARGS, - "Return TRUE if the buffer is empty.\n" }, - - { "full", - (PyCFunction)xu_buffer_full, - METH_VARARGS, - "Return TRUE if the buffer is full.\n" }, - - { NULL, NULL, 0, NULL } -}; - -staticforward PyTypeObject xu_buffer_type; - -static PyObject *xu_buffer_new(PyObject *self, PyObject *args) -{ - xu_buffer_object *xub; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - xub = PyObject_New(xu_buffer_object, &xu_buffer_type); - - if ( (xub->buf = malloc(BUFSZ)) == NULL ) - { - PyObject_Del((PyObject *)xub); - return NULL; - } - - xub->prod = xub->cons = 0; - - return (PyObject *)xub; -} - -static PyObject *xu_buffer_getattr(PyObject *obj, char *name) -{ - return Py_FindMethod(xu_buffer_methods, obj, name); -} - -static void xu_buffer_dealloc(PyObject *self) -{ - xu_buffer_object *xub = (xu_buffer_object *)self; - free(xub->buf); - PyObject_Del(self); -} - -static PyTypeObject xu_buffer_type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, - "buffer", - sizeof(xu_buffer_object), - 0, - xu_buffer_dealloc, /* tp_dealloc */ - NULL, /* tp_print */ - xu_buffer_getattr, /* tp_getattr */ - NULL, /* tp_setattr */ - NULL, /* tp_compare */ - NULL, /* tp_repr */ - NULL, /* tp_as_number */ - NULL, /* tp_as_sequence */ - NULL, /* tp_as_mapping */ - NULL /* tp_hash */ -}; - - - -/* - * *********************** MODULE WRAPPER *********************** - */ - -static void handle_child_death(int dummy) -{ - while ( waitpid(-1, NULL, WNOHANG) > 0 ) - continue; -} - -static PyObject *xu_autoreap(PyObject *self, PyObject *args) -{ - struct sigaction sa; - - if ( !PyArg_ParseTuple(args, "") ) - return NULL; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = handle_child_death; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; - (void)sigaction(SIGCHLD, &sa, NULL); - - Py_INCREF(Py_None); - return Py_None; -} - -static PyMethodDef xu_methods[] = { - { "notifier", xu_notifier_new, METH_VARARGS, - "Create a new notifier." }, - { "message", xu_message_new, METH_VARARGS, - "Create a new communications message." }, - { "port", xu_port_new, METH_VARARGS, - "Create a new communications port." }, - { "buffer", xu_buffer_new, METH_VARARGS, - "Create a new ring buffer." }, - { "autoreap", xu_autoreap, METH_VARARGS, - "Ensure that zombie children are automatically reaped by the OS." }, - { NULL, NULL, 0, NULL } -}; - -PyMODINIT_FUNC initutils(void) -{ - PyObject *m, *d; - - m = Py_InitModule("xend.utils", xu_methods); - - d = PyModule_GetDict(m); - port_error = PyErr_NewException("xend.utils.PortError", NULL, NULL); - PyDict_SetItemString(d, "PortError", port_error); -} diff --git a/tools/xend/setup.py b/tools/xend/setup.py deleted file mode 100644 index 66ed7a937e..0000000000 --- a/tools/xend/setup.py +++ /dev/null @@ -1,19 +0,0 @@ - -from distutils.core import setup, Extension - -utils = Extension("utils", - extra_compile_args = ["-fno-strict-aliasing"], - include_dirs = ["../xc/lib", - "../../xen/include/hypervisor-ifs", - "../../linux-xen-sparse/include"], - library_dirs = ["../xc/lib"], - libraries = ["xc"], - sources = ["lib/utils.c"]) - -setup(name = "xend", - version = "1.0", - packages = ["xend"], - package_dir = { "xend" : "lib" }, - ext_package = "xend", - ext_modules = [ utils ] - ) diff --git a/tools/xend/xend b/tools/xend/xend deleted file mode 100755 index 6e321bbad3..0000000000 --- a/tools/xend/xend +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -import os, sys, xend.main - -def main(): - if not sys.argv[1:]: - print 'usage: %s {start|stop|restart}' % sys.argv[0] - elif os.fork(): - pid, status = os.wait() - return status >> 8 - elif sys.argv[1] == 'start': - return xend.main.start_daemon() - elif sys.argv[1] == 'stop': - return xend.main.stop_daemon() - elif sys.argv[1] == 'restart': - return xend.main.stop_daemon() or xend.main.start_daemon() - else: - print 'not an option:', sys.argv[1] - return 1 - -if __name__ == '__main__': - sys.exit(main()) diff --git a/tools/xenmgr/Makefile b/tools/xenmgr/Makefile deleted file mode 100644 index 642cd2273e..0000000000 --- a/tools/xenmgr/Makefile +++ /dev/null @@ -1,19 +0,0 @@ - -all: - python setup.py build - -install: all - if [ "$(prefix)" = "" ]; then \ - python setup.py install; \ - elif [ "$(dist)" = "yes" ]; then \ - python setup.py install --home="$(prefix)"; \ - else \ - python setup.py install --root="$(prefix)"; \ - fi - mkdir -p $(prefix)/usr/sbin - install -m0755 xend $(prefix)/usr/sbin - install -m0755 netfix $(prefix)/usr/sbin - install -m0755 xm $(prefix)/usr/sbin - -clean: - rm -rf build *.pyc *.pyo *.o *.a *~ diff --git a/tools/xenmgr/lib/Args.py b/tools/xenmgr/lib/Args.py deleted file mode 100644 index 527e841d3d..0000000000 --- a/tools/xenmgr/lib/Args.py +++ /dev/null @@ -1,126 +0,0 @@ -import sxp - -class ArgError(StandardError): - pass - -class Args: - """Argument encoding support for HTTP. - """ - - def __init__(self, paramspec, keyspec): - self.arg_ord = [] - self.arg_dict = {} - self.key_ord = [] - self.key_dict = {} - for (name, type) in paramspec: - self.arg_ord.append(name) - self.arg_dict[name] = type - for (name, type) in keyspec: - self.key_ord.append(name) - self.key_dict[name] = type - - def get_args(self, d, xargs=None): - args = {} - keys = {} - params = [] - if xargs: - self.split_args(xargs, args, keys) - self.split_args(d, args, keys) - for a in self.arg_ord: - if a in args: - params.append(args[a]) - else: - raise ArgError('Missing parameter: %s' % a) - return (params, keys) - - def split_args(self, d, args, keys): - for (k, v) in d.items(): - if k in self.arg_dict: - type = self.arg_dict[k] - val = self.coerce(type, v) - args[k] = val - elif k in self.key_dict: - type = self.key_dict[k] - val = self.coerce(type, v) - keys[k] = val - else: - raise ArgError('Invalid parameter: %s' % k) - - def get_form_args(self, f, xargs=None): - d = {} - for (k, v) in f.items(): - n = len(v) - if ((k not in self.arg_dict) and - (k not in self.key_dict)): - continue - if n == 0: - continue - elif n == 1: - d[k] = v[0] - else: - raise ArgError('Too many values for %s' % k) - return self.get_args(d, xargs=xargs) - - def coerce(self, type, v): - try: - if type == 'int': - return int(v) - if type == 'str': - return str(v) - if type == 'sxpr': - return self.sxpr(v) - except ArgError: - raise - except StandardError, ex: - raise ArgError(str(ex)) - - def sxpr(self, v): - if instanceof(v, types.ListType): - return v - if instanceof(v, types.File) or hasattr(v, 'readline'): - return sxpr_file(v) - if instanceof(v, types.StringType): - return sxpr_file(StringIO(v)) - return str(v) - - def sxpr_file(self, fin): - try: - vals = sxp.parse(fin) - except: - raise ArgError('Coercion to sxpr failed') - if len(vals) == 1: - return vals[0] - else: - raise ArgError('Too many sxprs') - - def call_with_args(self, fn, args, xargs=None): - (params, keys) = self.get_args(args, xargs=xargs) - fn(*params, **keys) - - def call_with_form_args(self, fn, fargs, xargs=None): - (params, keys) = self.get_form_args(fargs, xargs=xargs) - fn(*params, **keys) - -class ArgFn(Args): - """Represent a remote HTTP operation as a function. - Used on the client. - """ - - def __init__(self, fn, paramspec, keyspec={}): - Args.__init__(self, paramspec, keyspec) - self.fn = fn - - def __call__(self, fargs, xargs=None): - return self.call_with_args(self.fn, fargs, xargs=xargs) - -class FormFn(Args): - """Represent an operation as a function over a form. - Used in the HTTP server. - """ - - def __init__(self, fn, paramspec, keyspec={}): - Args.__init__(self, paramspec, keyspec) - self.fn = fn - - def __call__(self, fargs, xargs=None): - return self.call_with_form_args(self.fn, fargs, xargs=xargs) diff --git a/tools/xenmgr/lib/EventServer.py b/tools/xenmgr/lib/EventServer.py deleted file mode 100644 index 20c567ada7..0000000000 --- a/tools/xenmgr/lib/EventServer.py +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright (C) 2004 Mike Wray -"""Simple publish/subscribe event server. - -""" -import string - -from twisted.internet import reactor - -# subscribe a.b.c h: map a.b.c -> h -# subscribe a.b.* h: map a.b.* -> h -# subscribe a.b.? h: map a.b.? -> h -# -# for event a.b.c.d: -# -# lookup a.b.c.d, call handlers -# -# lookup a.b.c.?, call handlers -# -# lookup a.b.c.d.*, call handlers -# lookup a.b.c.*, call handlers -# lookup a.b.*, call handlers -# lookup a.*, call handlers -# lookup *, call handlers - -# a.b.c.d = (a b c d) -# a.b.c.? = (a b c _) -# a.b.c.* = (a b c . _) - -class EventServer: - - DOT = '.' - QUERY = '?' - DOT_QUERY = DOT + QUERY - STAR = '*' - DOT_STAR = DOT + STAR - - def __init__(self, run=0): - self.handlers = {} - self.run = run - self.queue = [] - - def start(self): - """Enable event handling. Sends any queued events. - """ - self.run = 1 - for (e,v) in self.queue: - self.inject(e, v) - self.queue = [] - - def stop(self): - """Suspend event handling. Events injected while suspended - are queued until we are started again. - """ - self.run = 0 - - def subscribe(self, event, handler): - """Subscribe to an event. For example 'a.b.c.d'. - A subcription like 'a.b.c.?' ending in '?' matches any value - for the '?'. A subscription like 'a.b.c.*' ending in '*' matches - any event type with the same prefix, 'a.b.c' in this case. - - event event name - handler event handler fn(event, val) - """ - hl = self.handlers.get(event) - if hl is None: - self.handlers[event] = [handler] - else: - hl.append(handler) - - def unsubscribe_all(self, event=None): - """Unsubscribe all handlers for a given event, or all handlers. - - event event (optional) - """ - if event == None: - self.handlers.clear() - elif event in self.handlers: - del self.handlers[event] - - def unsubscribe(self, event, handler): - """Unsubscribe a given event and handler. - - event event - handler handler - """ - hl = self.handlers.get(event) - if hl is None: - return - if handler in hl: - hl.remove(handler) - - def inject(self, event, val, async=1): - """Inject an event. Handlers for it are called if running, otherwise - it is queued. - - event event type - val event value - """ - if self.run: - if async: - reactor.callLater(0, self.call_handlers, event, val) - else: - self.notify_handlers(event, val) - else: - self.queue.append( (event, val) ) - - def call_handlers(self, event, val): - """Internal method to call event handlers. - """ - #print ">event", event, val - self.call_event_handlers(event, event, val) - self.call_query_handlers(event, val) - self.call_star_handlers(event, val) - - def call_event_handlers(self, key, event, val): - """Call the handlers for an event. - It is safe for handlers to subscribe or unsubscribe. - - key key for handler list - event event type - val event value - """ - hl = self.handlers.get(key) - if hl is None: - return - # Copy the handler list so that handlers can call - # subscribe/unsubscribe safely - python list iteration - # is not safe against list modification. - for h in hl[:]: - try: - h(event, val) - except: - pass - - def call_query_handlers(self, event, val): - """Call regex handlers for events matching 'event' that end in '?'. - - event event type - val event value - """ - dot_idx = event.rfind(self.DOT) - if dot_idx == -1: - self.call_event_handlers(self.QUERY, event, val) - else: - event_query = event[0:dot_idx] + self.DOT_QUERY - self.call_event_handlers(event_query, event, val) - - def call_star_handlers(self, event, val): - """Call regex handlers for events matching 'event' that end in '*'. - - event event type - val event value - """ - etype = string.split(event, self.DOT) - for i in range(len(etype), 0, -1): - event_star = self.DOT.join(etype[0:i]) + self.DOT_STAR - self.call_event_handlers(event_star, event, val) - self.call_event_handlers(self.STAR, event, val) - -def instance(): - global inst - try: - inst - except: - inst = EventServer() - inst.start() - return inst - -def main(): - def sys_star(event, val): - print 'sys_star', event, val - - def sys_foo(event, val): - print 'sys_foo', event, val - s.unsubscribe('sys.foo', sys_foo) - - def sys_foo2(event, val): - print 'sys_foo2', event, val - - def sys_bar(event, val): - print 'sys_bar', event, val - - def sys_foo_bar(event, val): - print 'sys_foo_bar', event, val - - def foo_bar(event, val): - print 'foo_bar', event, val - - s = EventServer() - s.start() - s.subscribe('sys.*', sys_star) - s.subscribe('sys.foo', sys_foo) - s.subscribe('sys.foo', sys_foo2) - s.subscribe('sys.bar', sys_bar) - s.subscribe('sys.foo.bar', sys_foo_bar) - s.subscribe('foo.bar', foo_bar) - s.inject('sys.foo', 'hello') - print - s.inject('sys.bar', 'hello again') - print - s.inject('sys.foo.bar', 'hello again') - print - s.inject('foo.bar', 'hello again') - print - s.inject('foo', 'hello again') - print - s.start() - s.unsubscribe('sys.*', sys_star) - s.unsubscribe_all('sys.*') - s.inject('sys.foo', 'hello') - -if __name__ == "__main__": - main() - diff --git a/tools/xenmgr/lib/EventTypes.py b/tools/xenmgr/lib/EventTypes.py deleted file mode 100644 index 6350baa5dd..0000000000 --- a/tools/xenmgr/lib/EventTypes.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -## XEND_DOMAIN_CREATE = "xend.domain.create": dom -## create: -## xend.domain.destroy: dom, reason:died/crashed -## xend.domain.up ? - -## xend.domain.unpause: dom -## xend.domain.pause: dom -## xend.domain.shutdown: dom -## xend.domain.destroy: dom - -## xend.domain.migrate.begin: dom, to -## Begin tells: src host, src domain uri, dst host. Dst id known? -## err: src host, src domain uri, dst host, dst id if known, status (of domain: ok, dead,...), reason -## end: src host, src domain uri, dst host, dst uri - -## Events for both ends of migrate: for exporter and importer? -## Include migrate id so can tie together. -## Have uri /xend/migrate/ for migrate info (migrations in progress). - -## (xend.domain.migrate.begin (src ) (src.domain ) -## (dst ) (id )) - -## xend.domain.migrate.end: -## (xend.domain.migrate.end (domain ) (to ) - -## xend.node.up: xend uri -## xend.node.down: xend uri - -## xend.error ? - -## format: - diff --git a/tools/xenmgr/lib/PrettyPrint.py b/tools/xenmgr/lib/PrettyPrint.py deleted file mode 100644 index 9e91b11448..0000000000 --- a/tools/xenmgr/lib/PrettyPrint.py +++ /dev/null @@ -1,299 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -"""General pretty-printer, including support for SXP. - -""" -import sys -import types -import StringIO -import sxp - -class PrettyItem: - - def __init__(self, width): - self.width = width - - def insert(self, block): - block.addtoline(self) - - def get_width(self): - return self.width - - def output(self, out): - print '***PrettyItem>output>', self - pass - - def prettyprint(self, out, width): - print '***PrettyItem>prettyprint>', self - return width - -class PrettyString(PrettyItem): - - def __init__(self, x): - PrettyItem.__init__(self, len(x)) - self.value = x - - def output(self, out): - out.write(self.value) - - def prettyprint(self, line): - line.output(self) - - def show(self, out): - print >> out, ("(string (width %d) '%s')" % (self.width, self.value)) - -class PrettySpace(PrettyItem): - - def output(self, out): - out.write(' ' * self.width) - - def prettyprint(self, line): - line.output(self) - - def show(self, out): - print >> out, ("(space (width %d))" % self.width) - -class PrettyBreak(PrettyItem): - - def __init__(self, width, indent): - PrettyItem.__init__(self, width) - self.indent = indent - self.space = 0 - self.active = 0 - - def output(self, out): - out.write(' ' * self.width) - - def prettyprint(self, line): - if line.breaks(self.space): - self.active = 1 - line.newline(self.indent) - else: - line.output(self) - - def show(self, out): - print >> out, ("(break (width %d) (indent %d) (space %d) (active %d))" - % (self.width, self.indent, self.space, self.lspace, self.active)) - -class PrettyNewline(PrettySpace): - - def __init__(self, indent): - PrettySpace.__init__(self, indent) - - def insert(self, block): - block.newline() - block.addtoline(self) - - def output(self, out): - out.write(' ' * self.width) - - def prettyprint(self, line): - line.newline(0) - line.output(self) - - def show(self, out): - print >> out, ("(nl (indent %d))" % self.indent) - -class PrettyLine(PrettyItem): - def __init__(self): - PrettyItem.__init__(self, 0) - self.content = [] - - def write(self, x): - self.content.append(x) - - def end(self): - width = 0 - lastwidth = 0 - lastbreak = None - for x in self.content: - if isinstance(x, PrettyBreak): - if lastbreak: - lastbreak.space = (width - lastwidth) - lastbreak = x - lastwidth = width - width += x.get_width() - if lastbreak: - lastbreak.space = (width - lastwidth) - self.width = width - - def prettyprint(self, line): - for x in self.content: - x.prettyprint(line) - - def show(self, out): - print >> out, '(LINE (width %d)' % self.width - for x in self.content: - x.show(out) - print >> out, ')' - -class PrettyBlock(PrettyItem): - - def __init__(self, all=0, parent=None): - self.width = 0 - self.lines = [] - self.parent = parent - self.indent = 0 - self.all = all - self.broken = 0 - self.newline() - - def add(self, item): - item.insert(self) - - def end(self): - self.width = 0 - for l in self.lines: - l.end() - if self.width < l.width: - self.width = l.width - - def breaks(self, n): - return self.all and self.broken - - def newline(self): - self.lines.append(PrettyLine()) - - def addtoline(self, x): - self.lines[-1].write(x) - - def prettyprint(self, line): - self.indent = line.used - line.block = self - if not line.fits(self.width): - self.broken = 1 - for l in self.lines: - l.prettyprint(line) - line.block = self.parent - - def show(self, out): - print >> out, ('(BLOCK (width %d) (indent %d) (all %d) (broken %d)' % - (self.width, self.indent, self.all, self.broken)) - for l in self.lines: - l.show(out) - print >> out, ')' - -class Line: - - def __init__(self, out, width): - self.out = out - self.width = width - self.used = 0 - self.space = self.width - - def newline(self, indent): - indent += self.block.indent - self.out.write('\n') - self.out.write(' ' * indent) - self.used = indent - self.space = self.width - self.used - - def fits(self, n): - return self.space - n >= 0 - - def breaks(self, n): - return self.block.breaks(n) or not self.fits(n) - - def output(self, x): - n = x.get_width() - self.space -= n - self.used += n - if self.space < 0: - self.space = 0 - x.output(self.out) - -class PrettyPrinter: - """A prettyprinter based on what I remember of Derek Oppen's - prettyprint algorithm from TOPLAS way back. - """ - - def __init__(self, width=40): - self.width = width - self.block = None - self.top = None - - def write(self, x): - self.block.add(PrettyString(x)) - - def add(self, item): - self.block.add(item) - - def addbreak(self, width=1, indent=4): - self.add(PrettyBreak(width, indent)) - - def addspace(self, width=1): - self.add(PrettySpace(width)) - - def addnl(self, indent=0): - self.add(PrettyNewline(indent)) - - def begin(self, all=0): - block = PrettyBlock(all=all, parent=self.block) - self.block = block - - def end(self): - self.block.end() - if self.block.parent: - self.block.parent.add(self.block) - else: - self.top = self.block - self.block = self.block.parent - - def prettyprint(self, out=sys.stdout): - line = Line(out, self.width) - self.top.prettyprint(line) - -class SXPPrettyPrinter(PrettyPrinter): - """An SXP prettyprinter. - """ - - def pstring(self, x): - io = StringIO.StringIO() - sxp.show(x, out=io) - io.seek(0) - val = io.getvalue() - io.close() - return val - - def pprint(self, l): - if isinstance(l, types.ListType): - self.begin(all=1) - self.write('(') - i = 0 - for x in l: - if(i): self.addbreak() - self.pprint(x) - i += 1 - self.addbreak(width=0, indent=0) - self.write(')') - self.end() - else: - self.write(self.pstring(l)) - -def prettyprint(sxpr, out=sys.stdout, width=80): - """Prettyprint an SXP form. - - sxpr s-expression - out destination - width maximum output width - """ - if isinstance(sxpr, types.ListType): - pp = SXPPrettyPrinter(width=width) - pp.pprint(sxpr) - pp.prettyprint(out=out) - else: - sxp.show(sxpr, out=out) - print >> out - -def main(): - pin = sxp.Parser() - while 1: - buf = sys.stdin.read(100) - pin.input(buf) - if buf == '': break - l = pin.get_val() - prettyprint(l, width=80) - -if __name__ == "__main__": - main() - diff --git a/tools/xenmgr/lib/XendBridge.py b/tools/xenmgr/lib/XendBridge.py deleted file mode 100644 index 10e2234eae..0000000000 --- a/tools/xenmgr/lib/XendBridge.py +++ /dev/null @@ -1,193 +0,0 @@ -"""Bridge control utilities. -""" -import os -import os.path -import re -import sys - -from xenmgr import XendRoot -xroot = XendRoot.instance() - -os.defpath = os.defpath + ':/sbin:/usr/sbin:/usr/local/sbin' -CMD_IFCONFIG = 'ifconfig' -CMD_ROUTE = 'route' -CMD_BRCTL = 'brctl' -CMD_IPTABLES = "iptables" - -DEFAULT_BRIDGE = 'nbe-br' -DEFAULT_INTERFACE = 'eth0' - -opts = None - -class Opts: - - def __init__(self, defaults): - for (k, v) in defaults.items(): - setattr(self, k, v) - pass - -def cmd(p, s): - """Print and execute command 'p' with args 's'. - """ - global opts - c = p + ' ' + s - if opts.verbose: print c - if not opts.dryrun: - os.system(c) - -def default_bridge(): - return xroot.get_config_value('bridge', DEFAULT_BRIDGE) - -def default_interface(): - return xroot.get_config_value('interface', DEFAULT_INTERFACE) - -def vif_dev(dom, vif): - """Return the name of the network interface for vif on domain dom. - """ - return "vif%d.%d" % (dom, vif) - -def vif_bridge_add(dom, vif, bridge=None): - """Add the network interface for vif on dom to a bridge. - """ - if not bridge: bridge = default_bridge() - d = { 'bridge': bridge, 'vif': vif_dev(dom, vif) } - cmd(CMD_BRCTL, 'addif %(bridge)s %(vif)s' % d) - return bridge - -def vif_bridge_rem(dom, vif, bridge=None): - """Remove the network interface for vif on dom from a bridge. - """ - if not bridge: bridge = default_bridge() - print 'vif_bridge_rem>', dom, vif, bridge - d = { 'bridge': bridge, 'vif': vif_dev(dom, vif) } - cmd(CMD_BRCTL, 'delif %(bridge)s %(vif)s' % d) - -def vif_restrict_addr(dom, vif, addr, delete=0): - d = { 'vif': vif_dev(dom, vif), 'addr': addr} - if delete: - d['flag'] = '-D' - else: - d['flag'] = '-A' - cmd(CMD_IPTABLES, '-P FORWARD DROP') - cmd(CMD_IPTABLES, '%(flag)s FORWARD -m physdev --physdev-in %(vif)s -s %(addr)s -j ACCEPT' % d) - cmd(CMD_IPTABLES, '%(flag)s FORWARD -m physdev --physdev-out %(vif)s -d %(addr)s -j ACCEPT' % d) - -def bridge_create(bridge=None, **kwd): - """Create a bridge. - Defaults hello time to 0, forward delay to 0 and stp off. - """ - if not bridge: bridge = default_bridge() - cmd(CMD_BRCTL, 'addbr %s' % bridge) - if kwd.get('hello', None) is None: - kwd['hello'] = 0 - if kwd.get('fd', None) is None: - kwd['fd'] = 0 - if kwd.get('stp', None) is None: - kwd['stp'] = 'off' - bridge_set(bridge, **kwd) - -def bridge_set(bridge, hello=None, fd=None, stp=None): - """Set bridge parameters. - """ - if hello is not None: - cmd(CMD_BRCTL, 'sethello %s %d' % (bridge, hello)) - if fd is not None: - cmd(CMD_BRCTL, 'setfd %s %d' % (bridge, fd)) - if stp is not None: - cmd(CMD_BRCTL, 'stp %s %s' % (bridge, stp)) - -def bridge_del(bridge=None): - """Delete a bridge. - """ - if not bridge: bridge = default_bridge() - cmd(CMD_BRCTL, 'delbr %s' % bridge) - -def routes(): - """Return a list of the routes. - """ - fin = os.popen(CMD_ROUTE + ' -n', 'r') - routes = [] - for x in fin: - if x.startswith('Kernel'): continue - if x.startswith('Destination'): continue - x = x.strip() - y = x.split() - z = { 'destination': y[0], - 'gateway' : y[1], - 'mask' : y[2], - 'flags' : y[3], - 'metric' : y[4], - 'ref' : y[5], - 'use' : y[6], - 'interface' : y[7] } - routes.append(z) - return routes - -def ifconfig(interface): - """Return the ip config for an interface, - """ - fin = os.popen(CMD_IFCONFIG + ' %s' % interface, 'r') - inetre = re.compile('\s*inet\s*addr:(?P
\S*)\s*Bcast:(?P\S*)\s*Mask:(?P\S*)') - info = None - for x in fin: - m = inetre.match(x) - if not m: continue - info = m.groupdict() - info['interface'] = interface - break - return info - -def reconfigure(interface=None, bridge=None): - """Reconfigure an interface to be attached to a bridge, and give the bridge - the IP address etc. from interface. Move the default route to the interface - to the bridge. - - If opts.create is true, creates the bridge. - """ - global opts - if not interface: interface = default_interface() - if not bridge: bridge = default_bridge() - intf_info = ifconfig(interface) - if not intf_info: - print 'Interface not found:', interface - return - if opts.create: - bridge_create(bridge) - #bridge_info = ifconfig(bridge) - #if not bridge_info: - # print 'Bridge not found:', bridge - # return - route_info = routes() - intf_info['bridge'] = bridge - intf_info['gateway'] = None - for r in route_info: - if (r['destination'] == '0.0.0.0' and - 'G' in r['flags'] and - r['interface'] == interface): - intf_info['gateway'] = r['gateway'] - if not intf_info['gateway']: - print 'Gateway not found: ', interface - return - cmd(CMD_IFCONFIG, - '%(bridge)s %(address)s netmask %(mask)s broadcast %(broadcast)s up' - % intf_info) - cmd(CMD_ROUTE, - 'add default gateway %(gateway)s dev %(bridge)s' - % intf_info) - cmd(CMD_BRCTL, 'addif %(bridge)s %(interface)s' % intf_info) - cmd(CMD_IFCONFIG, '%(interface)s 0.0.0.0' % intf_info) - -defaults = { - 'interface': default_interface(), - 'bridge' : default_bridge(), - 'verbose' : 1, - 'dryrun' : 0, - 'create' : 0, - } - -opts = Opts(defaults) - -def set_opts(val): - global opts - opts = val - return opts diff --git a/tools/xenmgr/lib/XendClient.py b/tools/xenmgr/lib/XendClient.py deleted file mode 100644 index 664835a155..0000000000 --- a/tools/xenmgr/lib/XendClient.py +++ /dev/null @@ -1,312 +0,0 @@ -# Copyright (C) 2004 Mike Wray -"""Client API for the HTTP interface on xend. -Callable as a script - see main(). -""" -import sys -import httplib -import types -from StringIO import StringIO -import urlparse - -from encode import * -import sxp -import PrettyPrint - -DEBUG = 0 - -class Foo(httplib.HTTPResponse): - - def begin(self): - fin = self.fp - while(1): - buf = fin.readline() - print "***", buf - if buf == '': - print - sys.exit() - - -def sxprio(sxpr): - io = StringIO() - sxp.show(sxpr, out=io) - print >> io - io.seek(0) - return io - -def fileof(val): - """Converter for passing configs. - Handles lists, files directly. - Assumes a string is a file name and passes its contents. - """ - if isinstance(val, types.ListType): - return sxprio(val) - if isinstance(val, types.StringType): - return file(val) - if hasattr(val, 'readlines'): - return val - -# todo: need to sort of what urls/paths are using for objects. -# e.g. for domains at the moment return '0'. -# should probably return abs path w.r.t. server, e.g. /xend/domain/0. -# As an arg, assume abs path is obj uri, otherwise just id. - -# Function to convert to full url: Xend.uri(path), e.g. -# maps /xend/domain/0 to http://wray-m-3.hpl.hp.com:8000/xend/domain/0 -# And should accept urls for ids? - -def urljoin(location, root, prefix='', rest=''): - prefix = str(prefix) - rest = str(rest) - base = 'http://' + location + root + prefix - url = urlparse.urljoin(base, rest) - return url - -def nodeurl(location, root, id=''): - return urljoin(location, root, 'node/', id) - -def domainurl(location, root, id=''): - return urljoin(location, root, 'domain/', id) - -def consoleurl(location, root, id=''): - return urljoin(location, root, 'console/', id) - -def deviceurl(location, root, id=''): - return urljoin(location, root, 'device/', id) - -def vneturl(location, root, id=''): - return urljoin(location, root, 'vnet/', id) - -def eventurl(location, root, id=''): - return urljoin(location, root, 'event/', id) - -def xend_request(url, method, data=None): - urlinfo = urlparse.urlparse(url) - (uproto, ulocation, upath, uparam, uquery, ufrag) = urlinfo - if DEBUG: print url, urlinfo - if uproto != 'http': - raise StandardError('Invalid protocol: ' + uproto) - if DEBUG: print '>xend_request', ulocation, upath, method, data - (hdr, args) = encode_data(data) - if data and method == 'GET': - upath += '?' + args - args = None - if method == "POST" and upath.endswith('/'): - upath = upath[:-1] - if DEBUG: print "ulocation=", ulocation, "upath=", upath, "args=", args - #hdr['User-Agent'] = 'Mozilla' - #hdr['Accept'] = 'text/html,text/plain' - conn = httplib.HTTPConnection(ulocation) - #conn.response_class = Foo - if DEBUG: conn.set_debuglevel(1) - conn.request(method, upath, args, hdr) - resp = conn.getresponse() - if DEBUG: print resp.status, resp.reason - if DEBUG: print resp.msg.headers - if resp.status in [204, 404]: - return None - if resp.status not in [200, 201, 202, 203]: - raise RuntimeError(resp.reason) - pin = sxp.Parser() - data = resp.read() - if DEBUG: print "***data" , data - if DEBUG: print "***" - pin.input(data); - pin.input_eof() - conn.close() - val = pin.get_val() - #if isinstance(val, types.ListType) and sxp.name(val) == 'val': - # val = val[1] - if isinstance(val, types.ListType) and sxp.name(val) == 'err': - raise RuntimeError(val[1]) - if DEBUG: print '**val='; sxp.show(val); print - return val - -def xend_get(url, args=None): - return xend_request(url, "GET", args) - -def xend_call(url, data): - return xend_request(url, "POST", data) - -class Xend: - - SRV_DEFAULT = "localhost:8000" - ROOT_DEFAULT = "/xend/" - - def __init__(self, srv=None, root=None): - self.bind(srv, root) - - def bind(self, srv=None, root=None): - if srv is None: srv = self.SRV_DEFAULT - if root is None: root = self.ROOT_DEFAULT - if not root.endswith('/'): root += '/' - self.location = srv - self.root = root - - def nodeurl(self, id=''): - return nodeurl(self.location, self.root, id) - - def domainurl(self, id=''): - return domainurl(self.location, self.root, id) - - def consoleurl(self, id=''): - return consoleurl(self.location, self.root, id) - - def deviceurl(self, id=''): - return deviceurl(self.location, self.root, id) - - def vneturl(self, id=''): - return vneturl(self.location, self.root, id) - - def eventurl(self, id=''): - return eventurl(self.location, self.root, id) - - def xend(self): - return xend_get(urljoin(self.location, self.root)) - - def xend_node(self): - return xend_get(self.nodeurl()) - - def xend_node_cpu_rrobin_slice_set(self, slice): - return xend_call(self.nodeurl(), - {'op' : 'cpu_rrobin_slice_set', - 'slice' : slice }) - - def xend_node_cpu_bvt_slice_set(self, slice): - return xend_call(self.nodeurl(), - {'op' : 'cpu_bvt_slice_set', - 'slice' : slice }) - - def xend_domains(self): - return xend_get(self.domainurl()) - - def xend_domain_create(self, conf): - return xend_call(self.domainurl(), - {'op' : 'create', - 'config' : fileof(conf) }) - - def xend_domain(self, id): - return xend_get(self.domainurl(id)) - - def xend_domain_unpause(self, id): - return xend_call(self.domainurl(id), - {'op' : 'unpause'}) - - def xend_domain_pause(self, id): - return xend_call(self.domainurl(id), - {'op' : 'pause'}) - - def xend_domain_shutdown(self, id): - return xend_call(self.domainurl(id), - {'op' : 'shutdown'}) - - def xend_domain_destroy(self, id): - return xend_call(self.domainurl(id), - {'op' : 'destroy'}) - - def xend_domain_save(self, id, filename): - return xend_call(self.domainurl(id), - {'op' : 'save', - 'file' : filename}) - - def xend_domain_restore(self, id, filename, conf): - return xend_call(self.domainurl(id), - {'op' : 'restore', - 'file' : filename, - 'config' : fileof(conf) }) - - def xend_domain_migrate(self, id, dst): - return xend_call(self.domainurl(id), - {'op' : 'migrate', - 'dst' : dst}) - - def xend_domain_pincpu(self, id, cpu): - return xend_call(self.domainurl(id), - {'op' : 'pincpu', - 'cpu' : cpu}) - - def xend_domain_cpu_bvt_set(self, id, mcuadv, warp, warpl, warpu): - return xend_call(self.domainurl(id), - {'op' : 'cpu_bvt_set', - 'mcuadv' : mvuadv, - 'warp' : warp, - 'warpl' : warpl, - 'warpu' : warpu }) - - def xend_domain_cpu_atropos_set(self, id, period, slice, latency, xtratime): - return xend_call(self.domainurl(id), - {'op' : 'cpu_atropos_set', - 'period' : period, - 'slice' : slice, - 'latency' : latency, - 'xtratime': xtratime }) - - def xend_domain_vifs(self, id): - return xend_get(self.domainurl(id), - { 'op' : 'vifs' }) - - def xend_domain_vif_ip_add(self, id, vif, ipaddr): - return xend_call(self.domainurl(id), - {'op' : 'vif_ip_add', - 'vif' : vif, - 'ip' : ipaddr }) - - def xend_domain_vbds(self, id): - return xend_get(self.domainurl(id), - {'op' : 'vbds'}) - - def xend_domain_vbd(self, id, vbd): - return xend_get(self.domainurl(id), - {'op' : 'vbd', - 'vbd' : vbd}) - - def xend_consoles(self): - return xend_get(self.consoleurl()) - - def xend_console(self, id): - return xend_get(self.consoleurl(id)) - - def xend_vnets(self): - return xend_get(self.vneturl()) - - def xend_vnet_create(self, conf): - return xend_call(self.vneturl(), - {'op': 'create', 'config': fileof(conf) }) - - def xend_vnet(self, id): - return xend_get(self.vneturl(id)) - - def xend_vnet_delete(self, id): - return xend_call(self.vneturl(id), - {'op': 'delete'}) - - def xend_event_inject(self, sxpr): - val = xend_call(self.eventurl(), - {'op': 'inject', 'event': fileof(sxpr) }) - - -def main(argv): - """Call an API function: - - python XendClient.py fn args... - - The leading 'xend_' on the function can be omitted. - Example: - - > python XendClient.py domains - (domain 0 8) - > python XendClient.py domain 0 - (domain (id 0) (name Domain-0) (memory 128)) - """ - server = Xend() - fn = argv[1] - if not fn.startswith('xend'): - fn = 'xend_' + fn - args = argv[2:] - val = getattr(server, fn)(*args) - PrettyPrint.prettyprint(val) - print - -if __name__ == "__main__": - main(sys.argv) -else: - server = Xend() diff --git a/tools/xenmgr/lib/XendConsole.py b/tools/xenmgr/lib/XendConsole.py deleted file mode 100644 index dcf992d376..0000000000 --- a/tools/xenmgr/lib/XendConsole.py +++ /dev/null @@ -1,179 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -import socket -import Xc -xc = Xc.new() - -import sxp -import XendRoot -xroot = XendRoot.instance() -import XendDB - -import EventServer -eserver = EventServer.instance() - -from xenmgr.server import SrvConsoleServer -xcd = SrvConsoleServer.instance() - -class XendConsoleInfo: - """Console information record. - """ - - def __init__(self, console, dom1, port1, dom2, port2, conn=None): - self.console = console - self.dom1 = int(dom1) - self.port1 = int(port1) - self.dom2 = int(dom2) - self.port2 = int(port2) - self.conn = conn - #self.id = "%d.%d-%d.%d" % (self.dom1, self.port1, self.dom2, self.port2) - self.id = str(port1) - - def __str__(self): - s = "console" - s += " id=%s" % self.id - s += " src=%d.%d" % (self.dom1, self.port1) - s += " dst=%d.%d" % (self.dom2, self.port2) - s += " port=%s" % self.console - if self.conn: - s += " conn=%s:%s" % (self.conn[0], self.conn[1]) - return s - - def sxpr(self): - sxpr = ['console', - ['id', self.id], - ['src', self.dom1, self.port1], - ['dst', self.dom2, self.port2], - ['port', self.console], - ] - if self.conn: - sxpr.append(['connected', self.conn[0], self.conn[1]]) - return sxpr - - def connection(self): - return self.conn - - def update(self, consinfo): - conn = sxp.child(consinfo, 'connected') - if conn: - self.conn = conn[1:] - else: - self.conn = None - - def uri(self): - """Get the uri to use to connect to the console. - This will be a telnet: uri. - - return uri - """ - host = socket.gethostname() - return "telnet://%s:%s" % (host, self.console) - -class XendConsole: - - dbpath = "console" - - def __init__(self): - self.db = XendDB.XendDB(self.dbpath) - self.console = {} - self.console_db = self.db.fetchall("") - if xroot.get_rebooted(): - print 'XendConsole> rebooted: removing all console info' - self.rm_all() - eserver.subscribe('xend.domain.died', self.onDomainDied) - eserver.subscribe('xend.domain.destroy', self.onDomainDied) - - def rm_all(self): - """Remove all console info. Used after reboot. - """ - for (k, v) in self.console_db.items(): - self._delete_console(k) - - def refresh(self): - consoles = xcd.consoles() - cons = {} - for consinfo in consoles: - id = str(sxp.child_value(consinfo, 'id')) - cons[id] = consinfo - if id not in self.console: - self._new_console(consinfo) - for c in self.console.values(): - consinfo = cons.get(c.id) - if consinfo: - c.update(consinfo) - else: - self._delete_console(c.id) - - def onDomainDied(self, event, val): - dom = int(val) - #print 'XendConsole>onDomainDied', 'event', event, "dom=", dom - for c in self.consoles(): - #print 'onDomainDied', "dom=", dom, "dom1=", c.dom1, "dom2=", c.dom2 - if (c.dom1 == dom) or (c.dom2 == dom): - 'XendConsole>onDomainDied', 'delete console dom=', dom - ctrl = xcd.get_domain_console(dom) - if ctrl: - ctrl.close() - self._delete_console(c.id) - - def sync(self): - self.db.saveall("", self.console_db) - - def sync_console(self, id): - self.db.save(id, self.console_db[id]) - - def _new_console(self, consinfo): - # todo: xen needs a call to get current domain id. - dom1 = 0 - port1 = sxp.child_value(consinfo, 'local_port') - dom2 = sxp.child_value(consinfo, 'domain') - port2 = sxp.child_value(consinfo, 'remote_port') - console = sxp.child_value(consinfo, 'console_port') - info = XendConsoleInfo(console, dom1, int(port1), int(dom2), int(port2)) - info.update(consinfo) - self._add_console(info.id, info) - return info - - def _add_console(self, id, info): - self.console[id] = info - self.console_db[id] = info.sxpr() - self.sync_console(id) - - def _delete_console(self, id): - if id in self.console: - del self.console[id] - if id in self.console_db: - del self.console_db[id] - self.db.delete(id) - - def console_ls(self): - self.refresh() - return self.console.keys() - - def consoles(self): - self.refresh() - return self.console.values() - - def console_create(self, dom): - consinfo = xcd.console_create(dom) - info = self._new_console(consinfo) - return info - - def console_get(self, id): - self.refresh() - return self.console.get(id) - - def console_delete(self, id): - self._delete_console(id) - - def console_disconnect(self, id): - id = int(id) - xcd.console_disconnect(id) - -def instance(): - global inst - try: - inst - except: - inst = XendConsole() - return inst diff --git a/tools/xenmgr/lib/XendDB.py b/tools/xenmgr/lib/XendDB.py deleted file mode 100644 index 6a27e65b58..0000000000 --- a/tools/xenmgr/lib/XendDB.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -import os -import os.path -import errno -import dircache -import time - -import sxp -import XendRoot -xroot = XendRoot.instance() - -class XendDB: - """Persistence for Xend. Stores data in files and directories. - """ - - def __init__(self, path=None): - self.dbpath = xroot.get_dbroot() - if path: - self.dbpath = os.path.join(self.dbpath, path) - pass - - def filepath(self, path): - return os.path.join(self.dbpath, path) - - def fetch(self, path): - fpath = self.filepath(path) - return self.fetchfile(fpath) - - def fetchfile(self, fpath): - pin = sxp.Parser() - fin = file(fpath, "rb") - try: - while 1: - try: - buf = fin.read(1024) - except IOError, ex: - if ex.errno == errno.EINTR: - continue - else: - raise - pin.input(buf) - if buf == '': - pin.input_eof() - break - finally: - fin.close() - return pin.get_val() - - def save(self, path, sxpr): - fpath = self.filepath(path) - return self.savefile(fpath, sxpr) - - def savefile(self, fpath, sxpr): - fdir = os.path.dirname(fpath) - if not os.path.isdir(fdir): - os.makedirs(fdir) - fout = file(fpath, "wb+") - try: - t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) - fout.write("# %s %s\n" % (fpath, t)) - sxp.show(sxpr, out=fout) - finally: - fout.close() - - def fetchall(self, path): - dpath = self.filepath(path) - d = {} - for k in dircache.listdir(dpath): - try: - v = self.fetchfile(os.path.join(dpath, k)) - d[k] = v - except: - pass - return d - - def saveall(self, path, d): - for (k, v) in d.items(): - self.save(os.path.join(path, k), v) - - def delete(self, path): - dpath = self.filepath(path) - os.unlink(dpath) - - def ls(self, path): - dpath = self.filepath(path) - return dircache.listdir(dpath) - - - - diff --git a/tools/xenmgr/lib/XendDomain.py b/tools/xenmgr/lib/XendDomain.py deleted file mode 100644 index 2eaa9b6241..0000000000 --- a/tools/xenmgr/lib/XendDomain.py +++ /dev/null @@ -1,366 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -"""Handler for domain operations. - Nothing here is persistent (across reboots). - Needs to be persistent for one uptime. -""" -import sys - -from twisted.internet import defer - -import Xc; xc = Xc.new() -import xenctl.ip - -import sxp -import XendRoot -xroot = XendRoot.instance() -import XendDB -import XendDomainInfo -import XendConsole -import EventServer - -from xenmgr.server import SrvConsoleServer -xend = SrvConsoleServer.instance() - -eserver = EventServer.instance() - -__all__ = [ "XendDomain" ] - -class XendDomain: - """Index of all domains. Singleton. - """ - - dbpath = "domain" - domain = {} - - def __init__(self): - self.xconsole = XendConsole.instance() - # Table of domain info indexed by domain id. - self.db = XendDB.XendDB(self.dbpath) - #self.domain = {} - self.domain_db = self.db.fetchall("") - if xroot.get_rebooted(): - print 'XendDomain> rebooted: removing all domain info' - self.rm_all() - eserver.subscribe('xend.virq', self.onVirq) - self.initial_refresh() - - def onVirq(self, event, val): - print 'XendDomain> virq', val - self.reap() - - def rm_all(self): - """Remove all domain info. Used after reboot. - """ - for (k, v) in self.domain_db.items(): - self._delete_domain(k, notify=0) - - def initial_refresh(self): - """Refresh initial domain info from domain_db. - """ - print "initial_refresh>" - for d in self.domain_db.values(): print 'db dom=', d - domlist = xc.domain_getinfo() - for d in domlist: print 'xc dom=', d - doms = {} - for d in domlist: - domid = str(d['dom']) - doms[domid] = d - dlist = [] - for config in self.domain_db.values(): - domid = str(sxp.child_value(config, 'id')) - print "dom=", domid, "config=", config - if domid in doms: - print "dom=", domid, "new" - deferred = self._new_domain(config, doms[domid]) - dlist.append(deferred) - else: - print "dom=", domid, "del" - self._delete_domain(domid) - deferred = defer.DeferredList(dlist, fireOnOneErrback=1) - def cbok(val): - #print "doms:" - #for d in self.domain.values(): print 'dom', d - self.refresh() - print "XendDomain>initial_refresh> doms:" - for d in self.domain.values(): print 'dom', d - deferred.addCallback(cbok) - - def sync(self): - """Sync domain db to disk. - """ - self.db.saveall("", self.domain_db) - - def sync_domain(self, dom): - """Sync info for a domain to disk. - - dom domain id (string) - """ - self.db.save(dom, self.domain_db[dom]) - - def close(self): - pass - - def _new_domain(self, savedinfo, info): - """Create a domain entry from saved info. - """ -## console = None -## kernel = None -## id = sxp.child_value(info, 'id') -## dom = int(id) -## name = sxp.child_value(info, 'name') -## memory = int(sxp.child_value(info, 'memory')) -## consoleinfo = sxp.child(info, 'console') -## if consoleinfo: -## consoleid = sxp.child_value(consoleinfo, 'id') -## console = self.xconsole.console_get(consoleid) -## if dom and console is None: -## # Try to connect a console. -## console = self.xconsole.console_create(dom) -## config = sxp.child(info, 'config') -## if config: -## image = sxp.child(info, 'image') -## if image: -## image = sxp.child0(image) -## kernel = sxp.child_value(image, 'kernel') -## dominfo = XendDomainInfo.XendDomainInfo( -## config, dom, name, memory, kernel, console) - config = sxp.child_value(savedinfo, 'config') - deferred = XendDomainInfo.vm_recreate(config, info) - def fn(dominfo): - self.domain[dominfo.id] = dominfo - deferred.addCallback(fn) - return deferred - - def _add_domain(self, id, info, notify=1): - self.domain[id] = info - self.domain_db[id] = info.sxpr() - self.sync_domain(id) - if notify: eserver.inject('xend.domain.created', id) - - def _delete_domain(self, id, notify=1): - if id in self.domain: - if notify: eserver.inject('xend.domain.died', id) - del self.domain[id] - if id in self.domain_db: - del self.domain_db[id] - self.db.delete(id) - - def reap(self): - """Go through the domains looking for ones that have crashed or stopped. - Tidy them up. - """ - print 'XendDomain>reap>' - domlist = xc.domain_getinfo() - casualties = [] - for d in domlist: - #print 'dom', d - dead = 0 - dead = dead or (d['crashed'] or d['shutdown']) - dead = dead or (d['dying'] and - not(d['running'] or d['paused'] or d['blocked'])) - if dead: - casualties.append(d) - for d in casualties: - id = str(d['dom']) - print 'XendDomain>reap> died id=', id, d - dominfo = self.domain.get(id) - if not dominfo: continue - dominfo.died() - self.domain_destroy(id, refresh=0) - print 'XendDomain>reap<' - - def refresh(self): - """Refresh domain list from Xen. - """ - domlist = xc.domain_getinfo() - # Index the domlist by id. - # Add entries for any domains we don't know about. - doms = {} - for d in domlist: - id = str(d['dom']) - doms[id] = d - if id not in self.domain: - config = None - #image = None - #newinfo = XendDomainInfo.XendDomainInfo( - # config, d['dom'], d['name'], d['mem_kb']/1024, image=image, info=d) - deferred = XendDomainInfo.vm_recreate(config, d) - def fn(dominfo): - self._add_domain(dominfo.id, dominfo) - deferred.addCallback(fn) - # Remove entries for domains that no longer exist. - for d in self.domain.values(): - dominfo = doms.get(d.id) - if dominfo: - d.update(dominfo) - else: - self._delete_domain(d.id) - self.reap() - - def refresh_domain(self, id): - dom = int(id) - dominfo = xc.domain_getinfo(dom, 1) - if dominfo == [] or dominfo[0]['dom'] != dom: - try: - self._delete_domain(id) - except: - print 'refresh_domain: error' - raise - pass - else: - d = self.domain.get(id) - if d: - d.update(dominfo[0]) - - def domain_ls(self): - # List domains. - # Update info from kernel first. - self.refresh() - return self.domain.keys() - - def domains(self): - self.refresh() - return self.domain.values() - - def domain_create(self, config): - # Create domain, log it. - deferred = XendDomainInfo.vm_create(config) - def fn(dominfo): - self._add_domain(dominfo.id, dominfo) - return dominfo - deferred.addCallback(fn) - return deferred - - def domain_get(self, id): - id = str(id) - self.refresh_domain(id) - return self.domain[id] - - def domain_unpause(self, id): - """(Re)start domain running. - """ - dom = int(id) - eserver.inject('xend.domain.unpause', id) - return xc.domain_unpause(dom=dom) - - def domain_pause(self, id): - """Pause domain execution. - """ - dom = int(id) - eserver.inject('xend.domain.pause', id) - return xc.domain_pause(dom=dom) - - def domain_shutdown(self, id, reason='poweroff'): - """Shutdown domain (nicely). - """ - dom = int(id) - if dom <= 0: - return 0 - eserver.inject('xend.domain.shutdown', [id, reason]) - val = xend.domain_shutdown(dom, reason) - self.refresh() - return val - - def domain_destroy(self, id, refresh=1): - """Terminate domain immediately. - """ - dom = int(id) - if dom <= 0: - return 0 - eserver.inject('xend.domain.destroy', id) - val = xc.domain_destroy(dom=dom) - if refresh: self.refresh() - return val - - def domain_migrate(self, id, dst): - """Start domain migration. - """ - # Need a cancel too? - pass - - def domain_save(self, id, dst, progress=0): - """Save domain state to file, destroy domain. - """ - dom = int(id) - self.domain_pause(id) - eserver.inject('xend.domain.save', id) - rc = xc.linux_save(dom=dom, state_file=dst, progress=progress) - if rc == 0: - self.domain_destroy(id) - return rc - - def domain_restore(self, src, config, progress=0): - """Restore domain from file. - """ - dominfo = XendDomainInfo.dom_restore(dom, config) - self._add_domain(dominfo.id, dominfo) - return dominfo - - #============================================================================ - # Backward compatibility stuff from here on. - - def domain_pincpu(self, dom, cpu): - dom = int(dom) - return xc.domain_pincpu(dom, cpu) - - def domain_cpu_bvt_set(self, dom, mcuadv, warp, warpl, warpu): - dom = int(dom) - return xc.bvtsched_domain_set(dom=dom, mcuadv=mcuadv, - warp=warp, warpl=warpl, warpu=warpu) - - def domain_cpu_bvt_get(self, dom): - dom = int(dom) - return xc.bvtsched_domain_get(dom) - - def domain_cpu_atropos_set(self, dom, period, slice, latency, xtratime): - dom = int(dom) - return xc.atropos_domain_set(dom, period, slice, latency, xtratime) - - def domain_cpu_atropos_get(self, dom): - dom = int(dom) - return xc.atropos_domain_get(dom) - - def domain_vif_ls(self, dom): - dominfo = self.domain_get(dom) - if not dominfo: return None - devs = dominfo.get_devices('vif') - return range(0, len(devs)) - - def domain_vif_get(self, dom, vif): - dominfo = self.domain_get(dom) - if not dominfo: return None - return dominfo.get_device_by_index(vif) - - def domain_vif_ip_add(self, dom, vif, ip): - dom = int(dom) - return xenctl.ip.setup_vfr_rules_for_vif(dom, vif, ip) - - def domain_vbd_ls(self, dom): - dominfo = self.domain_get(dom) - if not dominfo: return [] - devs = dominfo.get_devices('vbd') - return [ sxp.child_value(v, 'dev') for v in devs ] - - def domain_vbd_get(self, dom, vbd): - dominfo = self.domain_get(dom) - if not dominfo: return None - devs = dominfo.get_devices('vbd') - for v in devs: - if sxp.child_value(v, 'dev') == vbd: - return v - return None - - def domain_shadow_control(self, dom, op): - dom = int(dom) - return xc.shadow_control(dom, op) - - #============================================================================ - -def instance(): - global inst - try: - inst - except: - inst = XendDomain() - return inst diff --git a/tools/xenmgr/lib/XendDomainConfig.py b/tools/xenmgr/lib/XendDomainConfig.py deleted file mode 100644 index 35db31ff51..0000000000 --- a/tools/xenmgr/lib/XendDomainConfig.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -"""Handler for persistent domain configs. - -""" - -import sxp -import XendDB -import XendDomain - -__all__ = [ "XendDomainConfig" ] - -class XendDomainConfig: - - dbpath = 'config' - - def __init__(self): - self.db = XendDB.XendDB(self.dbpath) - - def domain_config_ls(self, path): - return self.db.ls(path) - - def domain_config_create(self, path, sxpr): - self.db.save(path, sxpr) - pass - - def domain_config_delete(self, path): - self.db.delete(path) - - def domain_config_instance(self, path): - """Create a domain from a config. - """ - config = self.db.fetch(path) - xd = XendDomain.instance() - newdom = xd.domain_create(config) - return newdom - -def instance(): - global inst - try: - inst - except: - inst = XendDomainConfig() - return inst diff --git a/tools/xenmgr/lib/XendDomainInfo.py b/tools/xenmgr/lib/XendDomainInfo.py deleted file mode 100644 index 1f44f026a4..0000000000 --- a/tools/xenmgr/lib/XendDomainInfo.py +++ /dev/null @@ -1,911 +0,0 @@ -#!/usr/bin/python -# Copyright (C) 2004 Mike Wray - -"""Representation of a single domain. -Includes support for domain construction, using -open-ended configurations. - -Author: Mike Wray - -""" - -import string -import re -import sys -import os - -from twisted.internet import defer - -import Xc; xc = Xc.new() -import xenctl.ip - -import sxp - -import XendConsole -xendConsole = XendConsole.instance() - -import XendBridge - -import server.SrvConsoleServer -xend = server.SrvConsoleServer.instance() - -SIF_BLK_BE_DOMAIN = (1<<4) -SIF_NET_BE_DOMAIN = (1<<5) - -def readlines(fd): - """Version of readlines safe against EINTR. - """ - import errno - - lines = [] - while 1: - try: - line = fd.readline() - except IOError, ex: - if ex.errno == errno.EINTR: - continue - else: - raise - if line == '': break - lines.append(line) - return lines - -class VmError(ValueError): - """Vm construction error.""" - - def __init__(self, value): - self.value = value - - def __str__(self): - return self.value - - -def blkdev_name_to_number(name): - """Take the given textual block-device name (e.g., '/dev/sda1', - 'hda') and return the device number used by the OS. """ - - if not re.match( '/dev/', name ): - name = '/dev/' + name - - return os.stat(name).st_rdev - -def lookup_raw_partn(partition): - """Take the given block-device name (e.g., '/dev/sda1', 'hda') - and return a dictionary { device, start_sector, - nr_sectors, type } - device: Device number of the given partition - start_sector: Index of first sector of the partition - nr_sectors: Number of sectors comprising this partition - type: 'Disk' or identifying name for partition type - """ - - if not re.match( '/dev/', partition ): - partition = '/dev/' + partition - - drive = re.split( '[0-9]', partition )[0] - - if drive == partition: - fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' ) - line = readline(fd) - if line: - return [ { 'device' : blkdev_name_to_number(drive), - 'start_sector' : long(0), - 'nr_sectors' : long(line) * 2, - 'type' : 'Disk' } ] - return None - - # determine position on disk - fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' ) - - #['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012'] - lines = readlines(fd) - for line in lines: - m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' + - 'size=\s*([0-9]+), Id=\s*(\S+).*$', line) - if m: - return [ { 'device' : blkdev_name_to_number(drive), - 'start_sector' : long(m.group(1)), - 'nr_sectors' : long(m.group(2)), - 'type' : m.group(3) } ] - - return None - -def lookup_disk_uname(uname): - """Lookup a list of segments for a physical device. - uname [string]: name of the device in the format \'phy:dev\' for a physical device - returns [list of dicts]: list of extents that make up the named device - """ - ( type, d_name ) = string.split( uname, ':' ) - - if type == "phy": - segments = lookup_raw_partn( d_name ) - else: - segments = None - return segments - -def make_disk(dom, uname, dev, mode, recreate=0): - """Create a virtual disk device for a domain. - - @returns Deferred - """ - segments = lookup_disk_uname(uname) - if not segments: - raise VmError("vbd: Segments not found: uname=%s" % uname) - if len(segments) > 1: - raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname) - segment = segments[0] - vdev = blkdev_name_to_number(dev) - ctrl = xend.blkif_create(dom, recreate=recreate) - - def fn(ctrl): - return xend.blkif_dev_create(dom, vdev, mode, segment, recreate=recreate) - ctrl.addCallback(fn) - return ctrl - -def make_vif(dom, vif, vmac, recreate=0): - """Create a virtual network device for a domain. - - - @returns Deferred - """ - xend.netif_create(dom, recreate=recreate) - d = xend.netif_dev_create(dom, vif, vmac, recreate=recreate) - return d - -def vif_up(iplist): - """send an unsolicited ARP reply for all non link-local IP addresses. - - iplist IP addresses - """ - - IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind' - - def get_ip_nonlocal_bind(): - return int(open(IP_NONLOCAL_BIND, 'r').read()[0]) - - def set_ip_nonlocal_bind(v): - print >> open(IP_NONLOCAL_BIND, 'w'), str(v) - - def link_local(ip): - return xenctl.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0') - - def arping(ip, gw): - cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw) - print cmd - os.system(cmd) - - gateway = xenctl.ip.get_current_ipgw() or '255.255.255.255' - nlb = get_ip_nonlocal_bind() - if not nlb: set_ip_nonlocal_bind(1) - try: - for ip in iplist: - if not link_local(ip): - arping(ip, gateway) - finally: - if not nlb: set_ip_nonlocal_bind(0) - -config_handlers = {} - -def add_config_handler(name, h): - """Add a handler for a config field. - - name field name - h handler: fn(vm, config, field, index) - """ - config_handlers[name] = h - -def get_config_handler(name): - """Get a handler for a config field. - - returns handler or None - """ - return config_handlers.get(name) - -"""Table of handlers for virtual machine images. -Indexed by image type. -""" -image_handlers = {} - -def add_image_handler(name, h): - """Add a handler for an image type - name image type - h handler: fn(config, name, memory, image) - """ - image_handlers[name] = h - -def get_image_handler(name): - """Get the handler for an image type. - name image type - - returns handler or None - """ - return image_handlers.get(name) - -"""Table of handlers for devices. -Indexed by device type. -""" -device_handlers = {} - -def add_device_handler(name, h): - """Add a handler for a device type. - - name device type - h handler: fn(vm, dev) - """ - device_handlers[name] = h - -def get_device_handler(name): - """Get the handler for a device type. - - name device type - - returns handler or None - """ - return device_handlers.get(name) - -def vm_create(config): - """Create a VM from a configuration. - If a vm has been partially created and there is an error it - is destroyed. - - config configuration - - returns Deferred - raises VmError for invalid configuration - """ - print 'vm_create>' - vm = XendDomainInfo() - return vm.construct(config) - -def vm_recreate(config, info): - """Create the VM object for an existing domain. - """ - vm = XendDomainInfo() - vm.recreate = 1 - vm.setdom(info['dom']) - vm.name = info['name'] - vm.memory = info['mem_kb']/1024 - if config: - d = vm.construct(config) - else: - d = defer.Deferred() - d.callback(vm) - return d - -def vm_restore(src, config, progress=0): - """Restore a VM from a disk image. - - src saved state to restore - config configuration - progress progress reporting flag - returns deferred - raises VmError for invalid configuration - """ - vm = XendDomainInfo() - vm.config = config - ostype = "linux" #todo set from config - restorefn = getattr(xc, "%s_restore" % ostype) - dom = restorefn(state_file=src, progress=progress) - if dom < 0: - raise VMError('restore failed') - deferred = vm.dom_configure(dom) - def vifs_cb(val, vm): - vif_up(vm.ipaddrs) - deferred.addCallback(vifs_cb, vm) - return deferred - -def dom_get(dom): - domlist = xc.domain_getinfo(dom=dom) - if domlist and dom == domlist[0]['dom']: - return domlist[0] - return None - - -def append_deferred(dlist, v): - if isinstance(v, defer.Deferred): - dlist.append(v) - -def _vm_configure1(val, vm): - d = vm.create_devices() - print '_vm_configure1> made devices...' - def cbok(x): - print '_vm_configure1> cbok', x - return x - d.addCallback(cbok) - d.addCallback(_vm_configure2, vm) - print '_vm_configure1<' - return d - -def _vm_configure2(val, vm): - print '>callback _vm_configure2...' - d = vm.configure_fields() - def cbok(results): - print '_vm_configure2> cbok', results - return vm - def cberr(err): - print '_vm_configure2> cberr', err - vm.destroy() - return err - d.addCallback(cbok) - d.addErrback(cberr) - print '<_vm_configure2' - return d - -class XendDomainInfo: - """Virtual machine object.""" - - def __init__(self): - self.recreate = 0 - self.config = None - self.id = None - self.dom = None - self.name = None - self.memory = None - self.image = None - self.ramdisk = None - self.cmdline = None - self.console = None - self.devices = {} - self.configs = [] - self.info = None - self.ipaddrs = [] - self.blkif_backend = 0 - self.netif_backend = 0 - #todo: state: running, suspended - self.state = 'running' - #todo: set to migrate info if migrating - self.migrate = None - - def setdom(self, dom): - self.dom = int(dom) - self.id = str(dom) - - def update(self, info): - """Update with info from xc.domain_getinfo(). - """ - self.info = info - self.memory = self.info['mem_kb'] / 1024 - - def __str__(self): - s = "domain" - s += " id=" + self.id - s += " name=" + self.name - s += " memory=" + str(self.memory) - if self.console: - s += " console=" + self.console.id - if self.image: - s += " image=" + self.image - s += "" - return s - - __repr__ = __str__ - - def sxpr(self): - sxpr = ['domain', - ['id', self.id], - ['name', self.name], - ['memory', self.memory] ] - if self.info: - run = (self.info['running'] and 'r') or '-' - block = (self.info['blocked'] and 'b') or '-' - stop = (self.info['paused'] and 'p') or '-' - susp = (self.info['shutdown'] and 's') or '-' - crash = (self.info['crashed'] and 'c') or '-' - state = run + block + stop + susp + crash - sxpr.append(['state', state]) - if self.info['shutdown']: - reasons = ["poweroff", "reboot", "suspend"] - reason = reasons[self.info['shutdown_reason']] - sxpr.append(['shutdown_reason', reason]) - sxpr.append(['cpu', self.info['cpu']]) - sxpr.append(['cpu_time', self.info['cpu_time']/1e9]) - if self.console: - sxpr.append(self.console.sxpr()) - if self.config: - sxpr.append(['config', self.config]) - return sxpr - - def construct(self, config): - # todo - add support for scheduling params? - self.config = config - try: - self.name = sxp.child_value(config, 'name') - self.memory = int(sxp.child_value(config, 'memory', '128')) - self.configure_backends() - image = sxp.child_value(config, 'image') - image_name = sxp.name(image) - image_handler = get_image_handler(image_name) - if image_handler is None: - raise VmError('unknown image type: ' + image_name) - image_handler(self, image) - deferred = self.configure() - except StandardError, ex: - # Catch errors, cleanup and re-raise. - self.destroy() - raise - def cbok(x): - print 'vm_create> cbok', x - return x - deferred.addCallback(cbok) - print 'vm_create<' - return deferred - - def config_devices(self, name): - """Get a list of the 'device' nodes of a given type from the config. - - name device type - return list of device configs - """ - devices = [] - for d in sxp.children(self.config, 'device'): - dev = sxp.child0(d) - if dev is None: continue - if name == sxp.name(dev): - devices.append(dev) - return devices - - def add_device(self, type, dev): - """Add a device to a virtual machine. - - dev device to add - """ - dl = self.devices.get(type, []) - dl.append(dev) - self.devices[type] = dl - - def get_devices(self, type): - val = self.devices.get(type, []) - return val - - def get_device_by_id(self, type, id): - """Get the device with the given id. - - id device id - - returns device or None - """ - dl = self.get_devices(type) - for d in dl: - if d.getprop('id') == id: - return d - return None - - def get_device_by_index(self, type, idx): - """Get the device with the given index. - - idx device index - - returns device or None - """ - dl = self.get_devices(type) - if 0 <= idx < len(dl): - return dl[idx] - else: - return None - - def add_config(self, val): - """Add configuration data to a virtual machine. - - val data to add - """ - self.configs.append(val) - - def destroy(self): - if self.dom <= 0: - return 0 - return xc.domain_destroy(dom=self.dom) - - def died(self): - print 'died>', self.dom - self.release_devices() - - def release_devices(self): - print 'release_devices>', self.dom - self.release_vifs() - self.release_vbds() - self.devices = {} - - def release_vifs(self): - print 'release_vifs>', self.dom - if self.dom is None: return - ctrl = xend.netif_get(self.dom) - if ctrl: - ctrl.destroy() - - def release_vbds(self): - print 'release_vbds>', self.dom - if self.dom is None: return - ctrl = xend.blkif_get(self.dom) - if ctrl: - ctrl.destroy() - - def show(self): - """Print virtual machine info. - """ - print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory) - print "image:" - sxp.show(self.image) - print - for dl in self.devices: - for dev in dl: - print "device:" - sxp.show(dev) - print - for val in self.configs: - print "config:" - sxp.show(val) - print - print "]" - - def init_domain(self): - """Initialize the domain memory. - """ - if self.recreate: return - memory = self.memory - name = self.name - cpu = int(sxp.child_value(self.config, 'cpu', '-1')) - print 'init_domain>', memory, name, cpu - dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu) - if dom <= 0: - raise VmError('Creating domain failed: name=%s memory=%d' - % (name, memory)) - self.setdom(dom) - - def build_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n): - """Build the domain boot image. - """ - if self.recreate: return - if len(cmdline) >= 256: - print 'Warning: kernel cmdline too long' - dom = self.dom - buildfn = getattr(xc, '%s_build' % ostype) - print 'build_domain>', ostype, dom, kernel, cmdline, ramdisk - flags = 0 - if self.netif_backend: flags |= SIF_NET_BE_DOMAIN - if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN - err = buildfn(dom = dom, - image = kernel, - control_evtchn = self.console.port2, - cmdline = cmdline, - ramdisk = ramdisk, - flags = flags) - if err != 0: - raise VmError('Building domain failed: type=%s dom=%d err=%d' - % (ostype, dom, err)) - - def create_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n): - """Create a domain. Builds the image but does not configure it. - - ostype OS type - kernel kernel image - ramdisk kernel ramdisk - cmdline kernel commandline - vifs_n number of network interfaces - """ - print 'create_domain>', ostype, kernel - if not self.recreate: - if not os.path.isfile(kernel): - raise VmError('Kernel image does not exist: %s' % kernel) - if ramdisk and not os.path.isfile(ramdisk): - raise VMError('Kernel ramdisk does not exist: %s' % ramdisk) - print 'create-domain> init_domain...' - self.init_domain() - print 'create_domain>', 'dom=', self.dom - self.console = xendConsole.console_create(self.dom) - self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n) - self.image = kernel - self.ramdisk = ramdisk - self.cmdline = cmdline - - def create_devices(self): - """Create the devices for a vm. - - returns Deferred - raises VmError for invalid devices - """ - print '>create_devices' - dlist = [] - devices = sxp.children(self.config, 'device') - index = {} - for d in devices: - dev = sxp.child0(d) - if dev is None: - raise VmError('invalid device') - dev_name = sxp.name(dev) - dev_index = index.get(dev_name, 0) - dev_handler = get_device_handler(dev_name) - if dev_handler is None: - raise VmError('unknown device type: ' + dev_name) - v = dev_handler(self, dev, dev_index) - append_deferred(dlist, v) - index[dev_name] = dev_index + 1 - deferred = defer.DeferredList(dlist, fireOnOneErrback=1) - print ' created', dev - return id - defer.addCallback(fn) - return defer - -def vm_dev_vbd(vm, val, index): - """Create a virtual block device (vbd). - - vm virtual machine - val vbd config - index vbd index - """ - if vm.blkif_backend: - raise VmError('vbd: vbd in blkif backend domain') - vdev = index - uname = sxp.child_value(val, 'uname') - if not uname: - raise VMError('vbd: Missing uname') - dev = sxp.child_value(val, 'dev') - if not dev: - raise VMError('vbd: Missing dev') - mode = sxp.child_value(val, 'mode', 'r') - defer = make_disk(vm.dom, uname, dev, mode, vm.recreate) - def fn(vbd): - dev = xend.blkif_dev(vm.dom, vdev) - vm.add_device('vbd', dev) - return vbd - defer.addCallback(fn) - return defer - -def parse_pci(val): - if isinstance(val, StringType): - radix = 10 - if val.startswith('0x') or val.startswith('0X'): - radix = 16 - v = int(val, radix) - else: - v = val - return v - -def vm_dev_pci(vm, val, index): - bus = sxp.child_value(val, 'bus') - if not bus: - raise VMError('pci: Missing bus') - dev = sxp.child_value(val, 'dev') - if not dev: - raise VMError('pci: Missing dev') - func = sxp.child_value(val, 'func') - if not func: - raise VMError('pci: Missing func') - try: - bus = parse_pci(bus) - dev = parse_pci(dev) - func = parse_pci(func) - except: - raise VMError('pci: invalid parameter') - rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev, - func=func, enable=1) - if rc < 0: - #todo non-fatal - raise VMError('pci: Failed to configure device: bus=%s dev=%s func=%s' % - (bus, dev, func)) - return rc - - -def vm_field_vfr(vm, config, val, index): - """Handle a vfr field in a config. - - vm virtual machine - config vm config - val vfr field - """ - # Get the rules and add them. - # (vfr (vif (id foo) (ip x.x.x.x)) ... ) - list = sxp.children(val, 'vif') - ipaddrs = [] - for v in list: - id = sxp.child_value(v, 'id') - if id is None: - raise VmError('vfr: missing vif id') - id = int(id) - dev = vm.get_device_by_index('vif', id) - if not dev: - raise VmError('vfr: invalid vif id %d' % id) - vif = sxp.child_value(dev, 'vif') - ip = sxp.child_value(v, 'ip') - if not ip: - raise VmError('vfr: missing ip address') - ipaddrs.append(ip); - #Don't do this in new i/o model. - #print 'vm_field_vfr> add rule', 'dom=', vm.dom, 'vif=', vif, 'ip=', ip - #xenctl.ip.setup_vfr_rules_for_vif(vm.dom, vif, ip) - vm.ipaddrs = ipaddrs - -def vnet_bridge(vnet, vmac, dom, idx): - """Add the device for the vif to the bridge for its vnet. - """ - vif = "vif%d.%d" % (dom, idx) - try: - cmd = "(vif.conn (vif %s) (vnet %s) (vmac %s))" % (vif, vnet, vmac) - print "*** vnet_bridge>", cmd - out = file("/proc/vnet/policy", "wb") - out.write(cmd) - err = out.close() - print "vnet_bridge>", "err=", err - except IOError, ex: - print "vnet_bridge>", ex - -def vm_field_vnet(vm, config, val, index): - """Handle a vnet field in a config. - - vm virtual machine - config vm config - val vnet field - index index - """ - # Get the vif children. For each vif look up the vif device - # with the given id and configure its vnet. - # (vnet (vif (id foo) (vnet 2) (mac x:x:x:x:x:x)) ... ) - vif_vnets = sxp.children(val, 'vif') - for v in vif_vnets: - id = sxp.child_value(v, 'id') - if id is None: - raise VmError('vnet: missing vif id') - dev = vm.get_device_by_id('vif', id) - #vnet = sxp.child_value(v, 'vnet', 1) - #mac = sxp.child_value(dev, 'mac') - #vif = sxp.child_value(dev, 'vif') - #vnet_bridge(vnet, mac, vm.dom, 0) - #vm.add_config([ 'vif.vnet', ['id', id], ['vnet', vnet], ['mac', mac]]) - -# Register image handlers for linux and bsd. -add_image_handler('linux', vm_image_linux) -add_image_handler('netbsd', vm_image_netbsd) - -# Register device handlers for vifs and vbds. -add_device_handler('vif', vm_dev_vif) -add_device_handler('vbd', vm_dev_vbd) -add_device_handler('pci', vm_dev_pci) - -# Register config handlers for vfr and vnet. -add_config_handler('vfr', vm_field_vfr) -add_config_handler('vnet', vm_field_vnet) diff --git a/tools/xenmgr/lib/XendMigrate.py b/tools/xenmgr/lib/XendMigrate.py deleted file mode 100644 index 1580ba83ed..0000000000 --- a/tools/xenmgr/lib/XendMigrate.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -import sys -import socket - -import sxp -import XendDB -import EventServer; eserver = EventServer.instance() - -class XendMigrateInfo: - - # states: begin, active, failed, succeeded? - - def __init__(self, id, dom, dst): - self.id = id - self.state = 'begin' - self.src_host = socket.gethostname() - self.src_dom = dom - self.dst_host = dst - self.dst_dom = None - - def set_state(self, state): - self.state = state - - def get_state(self): - return self.state - - def sxpr(self): - sxpr = ['migrate', ['id', self.id], ['state', self.state] ] - sxpr_src = ['src', ['host', self.src_host], ['domain', self.src_dom] ] - sxpr.append(sxpr_src) - sxpr_dst = ['dst', ['host', self.dst] ] - if self.dst_dom: - sxpr_dst.append(['domain', self.dst_dom]) - sxpr.append(sxpr_dst) - return sxpr - - -class XendMigrate: - # Represents migration in progress. - # Use log for indications of begin/end/errors? - # Need logging of: domain create/halt, migrate begin/end/fail - # Log via event server? - - dbpath = "migrate" - - def __init__(self): - self.db = XendDB.XendDB(self.dbpath) - self.migrate = {} - self.migrate_db = self.db.fetchall("") - self.id = 0 - - def nextid(self): - self.id += 1 - return "%d" % self.id - - def sync(self): - self.db.saveall("", self.migrate_db) - - def sync_migrate(self, id): - self.db.save(id, self.migrate_db[id]) - - def close(self): - pass - - def _add_migrate(self, id, info): - self.migrate[id] = info - self.migrate_db[id] = info.sxpr() - self.sync_migrate(id) - #eserver.inject('xend.migrate.begin', info.sxpr()) - - def _delete_migrate(self, id): - #eserver.inject('xend.migrate.end', id) - del self.migrate[id] - del self.migrate_db[id] - self.db.delete(id) - - def migrate_ls(self): - return self.migrate.keys() - - def migrates(self): - return self.migrate.values() - - def migrate_get(self, id): - return self.migrate.get(id) - - def migrate_begin(self, dom, dst): - # Check dom for existence, not migrating already. - # Create migrate info, tell xend to migrate it? - # - or fork migrate command ourselves? - # Subscribe to migrate notifications (for updating). - id = self.nextid() - info = XenMigrateInfo(id, dom, dst) - self._add_migrate(id, info) - return id - -def instance(): - global inst - try: - inst - except: - inst = XendMigrate() - return inst diff --git a/tools/xenmgr/lib/XendNode.py b/tools/xenmgr/lib/XendNode.py deleted file mode 100644 index d293da8e77..0000000000 --- a/tools/xenmgr/lib/XendNode.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -"""Handler for node operations. - Has some persistent state: - - logs - - notification urls - -""" - -import os -import Xc - -class XendNode: - - def __init__(self): - self.xc = Xc.new() - - def shutdown(self): - return 0 - - def reboot(self): - return 0 - - def notify(self, uri): - return 0 - - def cpu_bvt_slice_set(self, slice): - ret = 0 - #ret = self.xc.bvtsched_global_set(ctx_allow=slice) - return ret - - def cpu_bvt_slice_get(self, slice): - ret = 0 - #ret = self.xc.bvtsched_global_get() - return ret - - def cpu_rrobin_slice_set(self, slice): - ret = 0 - #ret = self.xc.rrobin_global_set(slice) - return ret - - def info(self): - return self.nodeinfo() + self.physinfo() - - def nodeinfo(self): - (sys, host, rel, ver, mch) = os.uname() - return [['system', sys], - ['host', host], - ['release', rel], - ['version', ver], - ['machine', mch]] - - def physinfo(self): - pinfo = self.xc.physinfo() - info = [['cores', pinfo['cores']], - ['hyperthreads_per_core', pinfo['ht_per_core']], - ['cpu_mhz', pinfo['cpu_khz']/1000], - ['memory', pinfo['total_pages']/256], - ['free_memory', pinfo['free_pages']/256]] - return info - - - -def instance(): - global inst - try: - inst - except: - inst = XendNode() - return inst - diff --git a/tools/xenmgr/lib/XendRoot.py b/tools/xenmgr/lib/XendRoot.py deleted file mode 100644 index 665f5df29e..0000000000 --- a/tools/xenmgr/lib/XendRoot.py +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -"""Xend root class. -Creates the event server and handles configuration. -""" - -import os -import os.path -import sys -import EventServer - -# Initial create of the event server. -eserver = EventServer.instance() - -import sxp - -def reboots(): - """Get a list of system reboots from wtmp. - """ - out = os.popen('/usr/bin/last reboot', 'r') - list = [ x.strip() for x in out if x.startswith('reboot') ] - return list - -def last_reboot(): - """Get the last known system reboot. - """ - l = reboots() - return (l and l[-1]) or None - -class XendRoot: - """Root of the management classes.""" - - lastboot_default = "/var/xen/lastboot" - - """Default path to the root of the database.""" - dbroot_default = "/var/xen/xend-db" - - """Default path to the config file.""" - config_default = "/etc/xen/xend-config.sxp" - - """Environment variable used to override config_default.""" - config_var = "XEND_CONFIG" - - def __init__(self): - self.rebooted = 0 - self.last_reboot = None - self.dbroot = None - self.config_path = None - self.config = None - self.configure() - self.check_lastboot() - eserver.subscribe('xend.*', self.event_handler) - #eserver.subscribe('xend.domain.created', self.event_handler) - #eserver.subscribe('xend.domain.died', self.event_handler) - - def start(self): - eserver.inject('xend.start', self.rebooted) - - def event_handler(self, event, val): - print >> sys.stderr, "EVENT>", event, val - - def read_lastboot(self): - try: - val = file(self.lastboot, 'rb').readlines()[0] - except StandardError, ex: - print 'warning: Error reading', self.lastboot, ex - val = None - return val - - def write_lastboot(self, val): - if not val: return - try: - fdir = os.path.dirname(self.lastboot) - if not os.path.isdir(fdir): - os.makedirs(fdir) - out = file(self.lastboot, 'wb+') - out.write(val) - out.close() - except IOError, ex: - print 'warning: Error writing', self.lastboot, ex - pass - - def check_lastboot(self): - """Check if there has been a system reboot since we saved lastboot. - """ - last_val = self.read_lastboot() - this_val = last_reboot() - if this_val == last_val: - self.rebooted = 0 - else: - self.rebooted = 1 - self.write_lastboot(this_val) - self.last_reboot = this_val - - def get_last_reboot(self): - return self.last_reboot - - def get_rebooted(self): - return self.rebooted - - def configure(self): - self.set_config() - self.dbroot = self.get_config_value("dbroot", self.dbroot_default) - self.lastboot = self.get_config_value("lastboot", self.lastboot_default) - - def get_dbroot(self): - """Get the path to the database root. - """ - return self.dbroot - - def set_config(self): - """If the config file exists, read it. If not, ignore it. - - The config file is a sequence of sxp forms. - """ - self.config_path = os.getenv(self.config_var, self.config_default) - if os.path.exists(self.config_path): - fin = file(self.config_path, 'rb') - try: - config = sxp.parse(fin) - config.insert(0, 'config') - self.config = config - finally: - fin.close() - else: - self.config = ['config'] - - def get_config(self, name=None): - """Get the configuration element with the given name, or - the whole configuration if no name is given. - - name element name (optional) - returns config or none - """ - if name is None: - val = self.config - else: - val = sxp.child(self.config, name) - return val - - def get_config_value(self, name, val=None): - """Get the value of an atomic configuration element. - - name element name - val default value (optional, defaults to None) - returns value - """ - return sxp.child_value(self.config, name, val=val) - -def instance(): - global inst - try: - inst - except: - inst = XendRoot() - return inst diff --git a/tools/xenmgr/lib/XendVnet.py b/tools/xenmgr/lib/XendVnet.py deleted file mode 100644 index 213408e111..0000000000 --- a/tools/xenmgr/lib/XendVnet.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -"""Handler for vnet operations. -""" - -import sxp -import XendDB - -class XendVnet: - """Index of all vnets. Singleton. - """ - - dbpath = "vnet" - - def __init__(self): - # Table of vnet info indexed by vnet id. - self.vnet = {} - self.db = XendDB.XendDB(self.dbpath) - self.vnet = self.db.fetchall("") - - def vnet_ls(self): - """List all vnets. - """ - return self.vnet.keys() - - def vnets(self): - return self.vnet.values() - - def vnet_get(self, id): - """Get a vnet. - - id vnet id - """ - return self.vnet.get(id) - - def vnet_create(self, info): - """Create a vnet. - - info config - """ - self.vnet_configure(info) - - def vnet_configure(self, info): - """Configure a vnet. - id vnet id - info config - """ - # Need to configure for real. - # Only sync if succeeded - otherwise need to back out. - self.vnet[info.id] = info - self.db.save(info.id, info) - - def vnet_delete(self, id): - """Delete a vnet. - - id vnet id - """ - # Need to delete for real. What if fails? - if id in self.vnet: - del self.vnet[id] - self.db.delete(id) - -def instance(): - global inst - try: - inst - except: - inst = XendVnet() - return inst diff --git a/tools/xenmgr/lib/__init__.py b/tools/xenmgr/lib/__init__.py deleted file mode 100644 index 8d1c8b69c3..0000000000 --- a/tools/xenmgr/lib/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tools/xenmgr/lib/encode.py b/tools/xenmgr/lib/encode.py deleted file mode 100644 index 38c9351db7..0000000000 --- a/tools/xenmgr/lib/encode.py +++ /dev/null @@ -1,165 +0,0 @@ -# Copyright (C) 2004 Mike Wray -"""Encoding for arguments to HTTP calls. - Uses the url-encoding with MIME type 'application/x-www-form-urlencoded' - if the data does not include files. Otherwise it uses the encoding with - MIME type 'multipart/form-data'. See the HTML4 spec for details. - - """ -import sys -import types -from StringIO import StringIO - -import urllib -import httplib -import random -import md5 - -# Extract from HTML4 spec. -## The following example illustrates "multipart/form-data" -## encoding. Suppose we have the following form: - -##
-##

-## What is your name?
-## What files are you sending?
-## -##

- -## If the user enters "Larry" in the text input, and selects the text -## file "file1.txt", the user agent might send back the following data: - -## Content-Type: multipart/form-data; boundary=AaB03x - -## --AaB03x -## Content-Disposition: form-data; name="submit-name" - -## Larry -## --AaB03x -## Content-Disposition: form-data; name="files"; filename="file1.txt" -## Content-Type: text/plain - -## ... contents of file1.txt ... -## --AaB03x-- - -## If the user selected a second (image) file "file2.gif", the user agent -## might construct the parts as follows: - -## Content-Type: multipart/form-data; boundary=AaB03x - -## --AaB03x -## Content-Disposition: form-data; name="submit-name" - -## Larry -## --AaB03x -## Content-Disposition: form-data; name="files" -## Content-Type: multipart/mixed; boundary=BbC04y - -## --BbC04y -## Content-Disposition: file; filename="file1.txt" -## Content-Type: text/plain - -## ... contents of file1.txt ... -## --BbC04y -## Content-Disposition: file; filename="file2.gif" -## Content-Type: image/gif -## Content-Transfer-Encoding: binary - -## ...contents of file2.gif... -## --BbC04y-- -## --AaB03x-- - -__all__ = ['encode_data', 'encode_multipart', 'encode_form', 'mime_boundary' ] - -def data_values(d): - if isinstance(d, types.DictType): - return d.items() - else: - return d - -def encode_data(d): - """Encode some data for HTTP transport. - The encoding used is stored in 'Content-Type' in the headers. - - d data - sequence of tuples or dictionary - returns a 2-tuple of the headers and the encoded data - """ - val = ({}, None) - if d is None: return val - multipart = 0 - for (k, v) in data_values(d): - if encode_isfile(v): - multipart = 1 - break - if multipart: - val = encode_multipart(d) - else: - val = encode_form(d) - return val - -def encode_isfile(v): - if isinstance(v, types.FileType): - return 1 - if hasattr(v, 'readlines'): - return 1 - return 0 - -def encode_multipart(d): - boundary = mime_boundary() - hdr = { 'Content-Type': 'multipart/form-data; boundary=' + boundary } - out = StringIO() - for (k,v) in data_values(d): - out.write('--') - out.write(boundary) - out.write('\r\n') - if encode_isfile(v): - out.write('Content-Disposition: form-data; name="') - out.write(k) - if hasattr(v, 'name'): - out.write('"; filename="') - out.write(v.name) - out.write('"\r\n') - out.write('Content-Type: application/octet-stream\r\n') - out.write('\r\n') - for l in v.readlines(): - out.write(l) - else: - out.write('Content-Disposition: form-data; name="') - out.write(k) - out.write('"\r\n') - out.write('\r\n') - out.write(str(v)) - out.write('\r\n') - out.write('--') - out.write(boundary) - out.write('--') - out.write('\r\n') - return (hdr, out.getvalue()) - -def mime_boundary(): - random.seed() - m = md5.new() - for i in range(0, 10): - c = chr(random.randint(1, 255)) - m.update(c) - b = m.hexdigest() - return b[0:16] - -def encode_form(d): - hdr = { 'Content-Type': 'application/x-www-form-urlencoded' } - val = urllib.urlencode(d) - return (hdr, val) - -def main(): - #d = {'a': 1, 'b': 'x y', 'c': file('conf.sxp') } - #d = {'a': 1, 'b': 'x y' } - d = [ ('a', 1), ('b', 'x y'), ('c', file('conf.sxp')) ] - #d = [ ('a', 1), ('b', 'x y')] - v = encode_data(d) - print v[0] - sys.stdout.write(v[1]) - print - -if __name__ == "__main__": - main() diff --git a/tools/xenmgr/lib/server/SrvBase.py b/tools/xenmgr/lib/server/SrvBase.py deleted file mode 100644 index 722b60f49d..0000000000 --- a/tools/xenmgr/lib/server/SrvBase.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -import cgi - -import os -import sys -import types -import StringIO - -from twisted.internet import defer -from twisted.internet import reactor -from twisted.web import error -from twisted.web import resource -from twisted.web import server - -from xenmgr import sxp -from xenmgr import PrettyPrint - -def uri_pathlist(p): - """Split a path into a list. - p path - return list of path elements - """ - l = [] - for x in p.split('/'): - if x == '': continue - l.append(x) - return l - -class SrvBase(resource.Resource): - """Base class for services. - """ - - def parse_form(self, req, method): - """Parse the data for a request, GET using the URL, POST using encoded data. - Posts should use enctype='multipart/form-data' in the
tag, - rather than 'application/x-www-form-urlencoded'. Only 'multipart/form-data' - handles file upload. - - req request - returns a cgi.FieldStorage instance - """ - env = {} - env['REQUEST_METHOD'] = method - if self.query: - env['QUERY_STRING'] = self.query - val = cgi.FieldStorage(fp=req.rfile, headers=req.headers, environ=env) - return val - - def use_sxp(self, req): - """Determine whether to send an SXP response to a request. - Uses SXP if there is no User-Agent, no Accept, or application/sxp is in Accept. - - req request - returns 1 for SXP, 0 otherwise - """ - ok = 0 - user_agent = req.getHeader('User-Agent') - accept = req.getHeader('Accept') - if (not user_agent) or (not accept) or (accept.find(sxp.mime_type) >= 0): - ok = 1 - return ok - - def get_op_method(self, op): - """Get the method for an operation. - For operation 'foo' looks for 'op_foo'. - - op operation name - returns method or None - """ - op_method_name = 'op_' + op - return getattr(self, op_method_name, None) - - def perform(self, req): - """General operation handler for posted operations. - For operation 'foo' looks for a method op_foo and calls - it with op_foo(op, req). Replies with code 500 if op_foo - is not found. - - The method must return a list when req.use_sxp is true - and an HTML string otherwise (or list). - Methods may also return a Deferred (for incomplete processing). - - req request - """ - op = req.args.get('op') - if op is None or len(op) != 1: - req.setResponseCode(404, "Invalid") - return '' - op = op[0] - op_method = self.get_op_method(op) - if op_method is None: - req.setResponseCode(501, "Not implemented") - req.setHeader("Content-Type", "text/plain") - req.write("Not implemented: " + op) - return '' - else: - val = op_method(op, req) - if isinstance(val, defer.Deferred): - val.addCallback(self._cb_perform, req, 1) - return server.NOT_DONE_YET - else: - self._cb_perform(val, req, 0) - return '' - - def _cb_perform(self, val, req, dfr): - """Callback to complete the request. - May be called from a Deferred. - """ - if isinstance(val, error.ErrorPage): - req.write(val.render(req)) - elif self.use_sxp(req): - req.setHeader("Content-Type", sxp.mime_type) - sxp.show(val, req) - else: - req.write('') - self.print_path(req) - if isinstance(val, types.ListType): - req.write('
')
-                PrettyPrint.prettyprint(val, out=req)
-                req.write('
') - else: - req.write(str(val)) - req.write('') - if dfr: - req.finish() - - def print_path(self, req): - """Print the path with hyperlinks. - """ - pathlist = [x for x in req.prepath if x != '' ] - s = "/" - req.write('

/') - for x in pathlist: - s += x + "/" - req.write(' %s/' % (s, x)) - req.write("

") diff --git a/tools/xenmgr/lib/server/SrvConsole.py b/tools/xenmgr/lib/server/SrvConsole.py deleted file mode 100644 index ea25bb6113..0000000000 --- a/tools/xenmgr/lib/server/SrvConsole.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -from xenmgr import sxp -from xenmgr import XendConsole -from SrvDir import SrvDir - -class SrvConsole(SrvDir): - """An individual console. - """ - - def __init__(self, info): - SrvDir.__init__(self) - self.info = info - self.xc = XendConsole.instance() - - def op_disconnect(self, op, req): - val = self.xc.console_disconnect(self.info.id) - return val - - def render_POST(self, req): - return self.perform(req) - - def render_GET(self, req): - if self.use_sxp(req): - req.setHeader("Content-Type", sxp.mime_type) - sxp.show(self.info.sxpr(), out=req) - else: - req.write('') - self.print_path(req) - #self.ls() - req.write('

%s

' % self.info) - req.write('

Connect to domain %d

' - % (self.info.uri(), self.info.dom2)) - self.form(req) - req.write('') - return '' - - def form(self, req): - req.write('' % req.prePathURL()) - if self.info.connection(): - req.write('') - req.write('') diff --git a/tools/xenmgr/lib/server/SrvConsoleDir.py b/tools/xenmgr/lib/server/SrvConsoleDir.py deleted file mode 100644 index e5c0308f80..0000000000 --- a/tools/xenmgr/lib/server/SrvConsoleDir.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -from SrvDir import SrvDir -from SrvConsole import SrvConsole -from xenmgr import XendConsole -from xenmgr import sxp - -class SrvConsoleDir(SrvDir): - """Console directory. - """ - - def __init__(self): - SrvDir.__init__(self) - self.xconsole = XendConsole.instance() - - def console(self, x): - val = None - try: - info = self.xconsole.console_get(x) - val = SrvConsole(info) - except KeyError, ex: - print 'SrvConsoleDir>', ex - pass - return val - - def get(self, x): - v = SrvDir.get(self, x) - if v is not None: - return v - v = self.console(x) - return v - - def render_GET(self, req): - if self.use_sxp(req): - req.setHeader("Content-Type", sxp.mime_type) - self.ls_console(req, 1) - else: - req.write("") - self.print_path(req) - self.ls(req) - self.ls_console(req) - #self.form(req.wfile) - req.write("") - return '' - - def ls_console(self, req, use_sxp=0): - url = req.prePathURL() - if not url.endswith('/'): - url += '/' - if use_sxp: - consoles = self.xconsole.console_ls() - sxp.show(consoles, out=req) - else: - consoles = self.xconsole.consoles() - consoles.sort(lambda x, y: cmp(x.id, y.id)) - req.write('
    ') - for c in consoles: - req.write('
  • %s
  • ' % (url, c.id, c)) - req.write('
') diff --git a/tools/xenmgr/lib/server/SrvConsoleServer.py b/tools/xenmgr/lib/server/SrvConsoleServer.py deleted file mode 100644 index d6bb976b3c..0000000000 --- a/tools/xenmgr/lib/server/SrvConsoleServer.py +++ /dev/null @@ -1,706 +0,0 @@ -########################################################### -## Xen controller daemon -## Copyright (c) 2004, K A Fraser (University of Cambridge) -## Copyright (C) 2004, Mike Wray -########################################################### - -import os -import os.path -import signal -import sys -import socket -import pwd -import re -import StringIO - -from twisted.internet import pollreactor -pollreactor.install() - -from twisted.internet import reactor -from twisted.internet import protocol -from twisted.internet import abstract -from twisted.internet import defer - -import Xc; xc = Xc.new() - -import xend.utils - -from xenmgr import sxp -from xenmgr import PrettyPrint -from xenmgr import EventServer -eserver = EventServer.instance() - -from xenmgr.server import SrvServer - -import channel -import blkif -import netif -import console -import domain -from params import * - -DEBUG = 1 - -class MgmtProtocol(protocol.DatagramProtocol): - """Handler for the management socket (unix-domain). - """ - - def __init__(self, daemon): - #protocol.DatagramProtocol.__init__(self) - self.daemon = daemon - - def write(self, data, addr): - return self.transport.write(data, addr) - - def datagramReceived(self, data, addr): - if DEBUG: print 'datagramReceived> addr=', addr, 'data=', data - io = StringIO.StringIO(data) - try: - vals = sxp.parse(io) - res = self.dispatch(vals[0]) - self.send_result(addr, res) - except SystemExit: - raise - except: - if DEBUG: - raise - else: - self.send_error(addr) - - def send_reply(self, addr, sxpr): - io = StringIO.StringIO() - sxp.show(sxpr, out=io) - io.seek(0) - self.write(io.getvalue(), addr) - - def send_result(self, addr, res): - - def fn(res, self=self, addr=addr): - self.send_reply(addr, ['ok', res]) - - if isinstance(res, defer.Deferred): - res.addCallback(fn) - else: - fn(res) - - def send_error(self, addr): - (extype, exval) = sys.exc_info()[:2] - self.send_reply(addr, ['err', - ['type', str(extype) ], - ['value', str(exval) ] ] ) - - def opname(self, name): - """Get the name of the method for an operation. - """ - return 'op_' + name.replace('.', '_') - - def operror(self, name, v): - """Default operation handler - signals an error. - """ - raise NotImplementedError('Invalid operation: ' +name) - - def dispatch(self, req): - """Dispatch a request to its handler. - """ - op_name = sxp.name(req) - op_method_name = self.opname(op_name) - op_method = getattr(self, op_method_name, self.operror) - return op_method(op_name, req) - - def op_console_create(self, name, req): - """Create a new control interface - console for a domain. - """ - print name, req - dom = sxp.child_value(req, 'domain') - if not dom: raise ValueError('Missing domain') - dom = int(dom) - console_port = sxp.child_value(req, 'console_port') - if console_port: - console_port = int(console_port) - resp = self.daemon.console_create(dom, console_port) - print name, resp - return resp - - def op_consoles(self, name, req): - """Get a list of the consoles. - """ - return self.daemon.consoles() - - def op_console_disconnect(self, name, req): - id = sxp.child_value(req, 'id') - if not id: - raise ValueError('Missing console id') - id = int(id) - console = self.daemon.get_console(id) - if not console: - raise ValueError('Invalid console id') - if console.conn: - console.conn.loseConnection() - return ['ok'] - - def op_blkifs(self, name, req): - pass - - def op_blkif_devs(self, name, req): - pass - - def op_blkif_create(self, name, req): - pass - - def op_blkif_dev_create(self, name, req): - pass - - def op_netifs(self, name, req): - pass - - def op_netif_devs(self, name, req): - pass - - def op_netif_create(self, name, req): - pass - - def op_netif_dev_create(self, name, req): - pass - -class NotifierProtocol(protocol.Protocol): - """Asynchronous handler for i/o on the notifier (event channel). - """ - - def __init__(self, channelFactory): - self.channelFactory = channelFactory - - def notificationReceived(self, idx, type): - #print 'NotifierProtocol>notificationReceived>', idx, type - channel = self.channelFactory.getChannel(idx) - if not channel: - return - #print 'NotifierProtocol>notificationReceived> channel', channel - channel.notificationReceived(type) - - def connectionLost(self, reason=None): - pass - - def doStart(self): - pass - - def doStop(self): - pass - - def startProtocol(self): - pass - - def stopProtocol(self): - pass - -class NotifierPort(abstract.FileDescriptor): - """Transport class for the event channel. - """ - - def __init__(self, daemon, notifier, proto, reactor=None): - assert isinstance(proto, NotifierProtocol) - abstract.FileDescriptor.__init__(self, reactor) - self.daemon = daemon - self.notifier = notifier - self.protocol = proto - - def startListening(self): - self._bindNotifier() - self._connectToProtocol() - - def stopListening(self): - if self.connected: - result = self.d = defer.Deferred() - else: - result = None - self.loseConnection() - return result - - def fileno(self): - return self.notifier.fileno() - - def _bindNotifier(self): - self.connected = 1 - - def _connectToProtocol(self): - self.protocol.makeConnection(self) - self.startReading() - - def loseConnection(self): - if self.connected: - self.stopReading() - self.disconnecting = 1 - reactor.callLater(0, self.connectionLost) - - def connectionLost(self, reason=None): - abstract.FileDescriptor.connectionLost(self, reason) - if hasattr(self, 'protocol'): - self.protocol.doStop() - self.connected = 0 - #self.notifier.close() # Not implemented. - os.close(self.fileno()) - del self.notifier - if hasattr(self, 'd'): - self.d.callback(None) - del self.d - - def doRead(self): - #print 'NotifierPort>doRead>', self - count = 0 - while 1: - #print 'NotifierPort>doRead>', count - notification = self.notifier.read() - if not notification: - break - (idx, type) = notification - self.protocol.notificationReceived(idx, type) - self.notifier.unmask(idx) - count += 1 - #print 'NotifierPort>doRead<' - -class EventProtocol(protocol.Protocol): - """Asynchronous handler for a connected event socket. - """ - - def __init__(self, daemon): - #protocol.Protocol.__init__(self) - self.daemon = daemon - # Event queue. - self.queue = [] - # Subscribed events. - self.events = [] - self.parser = sxp.Parser() - self.pretty = 0 - - # For debugging subscribe to everything and make output pretty. - self.subscribe(['*']) - self.pretty = 1 - - def dataReceived(self, data): - try: - self.parser.input(data) - if self.parser.ready(): - val = self.parser.get_val() - res = self.dispatch(val) - self.send_result(res) - if self.parser.at_eof(): - self.loseConnection() - except SystemExit: - raise - except: - if DEBUG: - raise - else: - self.send_error() - - def loseConnection(self): - if self.transport: - self.transport.loseConnection() - if self.connected: - reactor.callLater(0, self.connectionLost) - - def connectionLost(self, reason=None): - self.unsubscribe() - - def send_reply(self, sxpr): - io = StringIO.StringIO() - if self.pretty: - PrettyPrint.prettyprint(sxpr, out=io) - else: - sxp.show(sxpr, out=io) - print >> io - io.seek(0) - return self.transport.write(io.getvalue()) - - def send_result(self, res): - return self.send_reply(['ok', res]) - - def send_error(self): - (extype, exval) = sys.exc_info()[:2] - return self.send_reply(['err', - ['type', str(extype)], - ['value', str(exval)]]) - - def send_event(self, val): - return self.send_reply(['event', val[0], val[1]]) - - def unsubscribe(self): - for event in self.events: - eserver.unsubscribe(event, self.queue_event) - - def subscribe(self, events): - self.unsubscribe() - for event in events: - eserver.subscribe(event, self.queue_event) - self.events = events - - def queue_event(self, name, v): - # Despite the name we dont' queue the event here. - # We send it because the transport will queue it. - self.send_event([name, v]) - - def opname(self, name): - return 'op_' + name.replace('.', '_') - - def operror(self, name, req): - raise NotImplementedError('Invalid operation: ' +name) - - def dispatch(self, req): - op_name = sxp.name(req) - op_method_name = self.opname(op_name) - op_method = getattr(self, op_method_name, self.operror) - return op_method(op_name, req) - - def op_help(self, name, req): - def nameop(x): - if x.startswith('op_'): - return x[3:].replace('_', '.') - else: - return x - - l = [ nameop(k) for k in dir(self) if k.startswith('op_') ] - return l - - def op_quit(self, name, req): - self.loseConnection() - - def op_exit(self, name, req): - sys.exit(0) - - def op_pretty(self, name, req): - self.pretty = 1 - return ['ok'] - - def op_console_disconnect(self, name, req): - id = sxp.child_value(req, 'id') - if not id: - raise ValueError('Missing console id') - self.daemon.console_disconnect(id) - return ['ok'] - - def op_info(self, name, req): - val = ['info'] - val += self.daemon.consoles() - val += self.daemon.blkifs() - val += self.daemon.netifs() - return val - - def op_sys_subscribe(self, name, v): - # (sys.subscribe event*) - # Subscribe to the events: - self.subscribe(v[1:]) - return ['ok'] - - def op_sys_inject(self, name, v): - # (sys.inject event) - event = v[1] - eserver.inject(sxp.name(event), event) - return ['ok'] - - -class EventFactory(protocol.Factory): - """Asynchronous handler for the event server socket. - """ - protocol = EventProtocol - service = None - - def __init__(self, daemon): - #protocol.Factory.__init__(self) - self.daemon = daemon - - def buildProtocol(self, addr): - proto = self.protocol(self.daemon) - proto.factory = self - return proto - -class VirqClient: - def __init__(self, daemon): - self.daemon = daemon - - def virqReceived(self, virq): - print 'VirqClient.virqReceived>', virq - eserver.inject('xend.virq', virq) - - def lostChannel(self, channel): - print 'VirqClient.lostChannel>', channel - -class Daemon: - """The xend daemon. - """ - def __init__(self): - self.shutdown = 0 - - def daemon_pids(self): - pids = [] - pidex = '(?P\d+)' - pythonex = '(?P\S*python\S*)' - cmdex = '(?P.*)' - procre = re.compile('^\s*' + pidex + '\s*' + pythonex + '\s*' + cmdex + '$') - xendre = re.compile('^/usr/sbin/xend\s*(start|restart)\s*.*$') - procs = os.popen('ps -e -o pid,args 2>/dev/null') - for proc in procs: - pm = procre.match(proc) - if not pm: continue - xm = xendre.match(pm.group('cmd')) - if not xm: continue - #print 'pid=', pm.group('pid'), 'cmd=', pm.group('cmd') - pids.append(int(pm.group('pid'))) - return pids - - def new_cleanup(self, kill=0): - err = 0 - pids = self.daemon_pids() - if kill: - for pid in pids: - print "Killing daemon pid=%d" % pid - os.kill(pid, signal.SIGHUP) - elif pids: - err = 1 - print "Daemon already running: ", pids - return err - - def cleanup(self, kill=False): - # No cleanup to do if PID_FILE is empty. - if not os.path.isfile(PID_FILE) or not os.path.getsize(PID_FILE): - return 0 - # Read the pid of the previous invocation and search active process list. - pid = open(PID_FILE, 'r').read() - lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines() - for line in lines: - if re.search('^ *' + pid + '.+xend', line): - if not kill: - print "Daemon is already running (pid %d)" % int(pid) - return 1 - # Old daemon is still active: terminate it. - os.kill(int(pid), 1) - # Delete the stale PID_FILE. - os.remove(PID_FILE) - return 0 - - def install_child_reaper(self): - #signal.signal(signal.SIGCHLD, self.onSIGCHLD) - # Ensure that zombie children are automatically reaped. - xend.utils.autoreap() - - def onSIGCHLD(self, signum, frame): - code = 1 - while code > 0: - code = os.waitpid(-1, os.WNOHANG) - - def start(self): - if self.cleanup(kill=False): - return 1 - - # Detach from TTY. - if not DEBUG: - os.setsid() - - if self.set_user(): - return 1 - - self.install_child_reaper() - - # Fork -- parent writes PID_FILE and exits. - pid = os.fork() - if pid: - # Parent - pidfile = open(PID_FILE, 'w') - pidfile.write(str(pid)) - pidfile.close() - return 0 - # Child - logfile = self.open_logfile() - self.redirect_output(logfile) - self.run() - return 0 - - def open_logfile(self): - if not os.path.exists(CONTROL_DIR): - os.makedirs(CONTROL_DIR) - - # Open log file. Truncate it if non-empty, and request line buffering. - if os.path.isfile(LOG_FILE): - os.rename(LOG_FILE, LOG_FILE+'.old') - logfile = open(LOG_FILE, 'w+', 1) - return logfile - - def set_user(self): - # Set the UID. - try: - os.setuid(pwd.getpwnam(USER)[2]) - return 0 - except KeyError, error: - print "Error: no such user '%s'" % USER - return 1 - - def redirect_output(self, logfile): - if DEBUG: return - # Close down standard file handles - try: - os.close(0) # stdin - os.close(1) # stdout - os.close(2) # stderr - except: - pass - # Redirect output to log file. - sys.stdout = sys.stderr = logfile - - def stop(self): - return self.cleanup(kill=True) - - def run(self): - self.createFactories() - self.listenMgmt() - self.listenEvent() - self.listenNotifier() - self.listenVirq() - SrvServer.create(bridge=1) - reactor.run() - - def createFactories(self): - self.channelF = channel.channelFactory() - self.domainCF = domain.DomainControllerFactory() - self.blkifCF = blkif.BlkifControllerFactory() - self.netifCF = netif.NetifControllerFactory() - self.consoleCF = console.ConsoleControllerFactory() - - def listenMgmt(self): - protocol = MgmtProtocol(self) - s = os.path.join(CONTROL_DIR, MGMT_SOCK) - if os.path.exists(s): - os.unlink(s) - return reactor.listenUNIXDatagram(s, protocol) - - def listenEvent(self): - protocol = EventFactory(self) - return reactor.listenTCP(EVENT_PORT, protocol) - - def listenNotifier(self): - protocol = NotifierProtocol(self.channelF) - p = NotifierPort(self, self.channelF.notifier, protocol, reactor) - p.startListening() - return p - - def listenVirq(self): - virqChan = self.channelF.virqChannel(channel.VIRQ_DOM_EXC) - virqChan.registerClient(VirqClient(self)) - - def exit(self): - reactor.diconnectAll() - sys.exit(0) - - def blkif_set_control_domain(self, dom, recreate=0): - """Set the block device backend control domain. - """ - return self.blkifCF.setControlDomain(dom, recreate=recreate) - - def blkif_get_control_domain(self, dom): - """Get the block device backend control domain. - """ - return self.blkifCF.getControlDomain() - - def blkif_create(self, dom, recreate=0): - """Create a block device interface controller. - - Returns Deferred - """ - d = self.blkifCF.createInstance(dom, recreate=recreate) - return d - - def blkifs(self): - return [ x.sxpr() for x in self.blkifCF.getInstances() ] - - def blkif_get(self, dom): - return self.blkifCF.getInstanceByDom(dom) - - def blkif_dev(self, dom, vdev): - return self.blkifCF.getDomainDevice(dom, vdev) - - def blkif_dev_create(self, dom, vdev, mode, segment, recreate=0): - """Create a block device. - - Returns Deferred - """ - ctrl = self.blkifCF.getInstanceByDom(dom) - if not ctrl: - raise ValueError('No blkif controller: %d' % dom) - print 'blkif_dev_create>', dom, vdev, mode, segment - d = ctrl.attachDevice(vdev, mode, segment, recreate=recreate) - return d - - def netif_set_control_domain(self, dom, recreate=0): - """Set the network interface backend control domain. - """ - return self.netifCF.setControlDomain(dom, recreate=recreate) - - def netif_get_control_domain(self, dom): - """Get the network interface backend control domain. - """ - return self.netifCF.getControlDomain() - - def netif_create(self, dom, recreate=0): - """Create a network interface controller. - - """ - return self.netifCF.createInstance(dom, recreate=recreate) - - def netifs(self): - return [ x.sxpr() for x in self.netifCF.getInstances() ] - - def netif_get(self, dom): - return self.netifCF.getInstanceByDom(dom) - - def netif_dev_create(self, dom, vif, vmac, recreate=0): - """Create a network device. - - todo - """ - ctrl = self.netifCF.getInstanceByDom(dom) - if not ctrl: - raise ValueError('No netif controller: %d' % dom) - d = ctrl.attachDevice(vif, vmac, recreate=recreate) - return d - - def netif_dev(self, dom, vif): - return self.netifCF.getDomainDevice(dom, vif) - - def console_create(self, dom, console_port=None): - """Create a console for a domain. - """ - console = self.consoleCF.getInstanceByDom(dom) - if console is None: - console = self.consoleCF.createInstance(dom, console_port) - return console.sxpr() - - def consoles(self): - return [ c.sxpr() for c in self.consoleCF.getInstances() ] - - def get_console(self, id): - return self.consoleCF.getInstance(id) - - def get_domain_console(self, dom): - return self.consoleCF.getInstanceByDom(dom) - - def console_disconnect(self, id): - """Disconnect any connected console client. - """ - console = self.get_console(id) - if not console: - raise ValueError('Invalid console id') - console.disconnect() - - def domain_shutdown(self, dom, reason): - """Shutdown a domain. - """ - ctrl = self.domainCF.getInstanceByDom(dom) - if not ctrl: - raise ValueError('No domain controller: %d' % dom) - ctrl.shutdown(reason) - return 0 - -def instance(): - global inst - try: - inst - except: - inst = Daemon() - return inst diff --git a/tools/xenmgr/lib/server/SrvDeviceDir.py b/tools/xenmgr/lib/server/SrvDeviceDir.py deleted file mode 100644 index 52f428540d..0000000000 --- a/tools/xenmgr/lib/server/SrvDeviceDir.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -from SrvDir import SrvDir - -class SrvDeviceDir(SrvDir): - """Device directory. - """ - - pass diff --git a/tools/xenmgr/lib/server/SrvDir.py b/tools/xenmgr/lib/server/SrvDir.py deleted file mode 100644 index f4310e279c..0000000000 --- a/tools/xenmgr/lib/server/SrvDir.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -from twisted.web import error -from xenmgr import sxp -from SrvBase import SrvBase - -class SrvConstructor: - """Delayed constructor for sub-servers. - Does not import the sub-server class or create the object until needed. - """ - - def __init__(self, klass): - """Create a constructor. It is assumed that the class - should be imported as 'import klass from klass'. - - klass name of its class - """ - self.klass = klass - self.obj = None - - def getobj(self): - """Get the sub-server object, importing its class and instantiating it if - necessary. - """ - if not self.obj: - exec 'from %s import %s' % (self.klass, self.klass) - klassobj = eval(self.klass) - self.obj = klassobj() - return self.obj - -class SrvDir(SrvBase): - """Base class for directory servlets. - """ - isLeaf = False - - def __init__(self): - SrvBase.__init__(self) - self.table = {} - self.order = [] - - def getChild(self, x, req): - if x == '': return self - val = self.get(x) - if val is None: - return error.NoResource('Not found') - else: - return val - - def get(self, x): - val = self.table.get(x) - if val is not None: - val = val.getobj() - return val - - def add(self, x, xclass = None): - if xclass is None: - xclass = 'SrvDir' - self.table[x] = SrvConstructor(xclass) - self.order.append(x) - - def render_GET(self, req): - if self.use_sxp(req): - req.setHeader("Content-type", sxp.mime_type) - self.ls(req, 1) - else: - req.write('') - self.print_path(req) - self.ls(req) - self.form(req) - req.write('') - return '' - - def ls(self, req, use_sxp=0): - url = req.prePathURL() - if not url.endswith('/'): - url += '/' - if use_sxp: - req.write('(ls ') - for k in self.order: - req.write(' ' + k) - req.write(')') - else: - req.write('
    ') - for k in self.order: - v = self.get(k) - req.write('
  • %s
  • ' - % (url, k, k)) - req.write('
') - - def form(self, req): - pass diff --git a/tools/xenmgr/lib/server/SrvDomain.py b/tools/xenmgr/lib/server/SrvDomain.py deleted file mode 100644 index 44d8f4cf6d..0000000000 --- a/tools/xenmgr/lib/server/SrvDomain.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -from xenmgr import sxp -from xenmgr import XendDomain -from xenmgr import XendConsole -from xenmgr import PrettyPrint -from xenmgr.Args import FormFn - -from SrvDir import SrvDir - -class SrvDomain(SrvDir): - """Service managing a single domain. - """ - - def __init__(self, dom): - SrvDir.__init__(self) - self.dom = dom - self.xd = XendDomain.instance() - self.xconsole = XendConsole.instance() - - def op_unpause(self, op, req): - val = self.xd.domain_unpause(self.dom.id) - return val - - def op_pause(self, op, req): - val = self.xd.domain_pause(self.dom.id) - return val - - def op_shutdown(self, op, req): - val = self.xd.domain_shutdown(self.dom.id) - req.setResponseCode(202) - req.setHeader("Location", "%s/.." % req.prePathURL()) - return val - - def op_destroy(self, op, req): - val = self.xd.domain_destroy(self.dom.id) - req.setHeader("Location", "%s/.." % req.prePathURL()) - return val - - def op_save(self, op, req): - fn = FormFn(self.xd.domain_save, - [['dom', 'int'], - ['file', 'str']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def op_restore(self, op, req): - fn = FormFn(self.xd.domain_restore, - [['dom', 'int'], - ['file', 'str']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def op_migrate(self, op, req): - fn = FormFn(self.xd.domain_migrate, - [['dom', 'int'], - ['destination', 'str']]) - val = fn(req.args, {'dom': self.dom.id}) - val = 0 # Some migrate id. - req.setResponseCode(202) - #req.send_header("Location", "%s/.." % self.path) # Some migrate url. - return val - - def op_pincpu(self, op, req): - fn = FormFn(self.xd.domain_migrate, - [['dom', 'int'], - ['cpu', 'int']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def op_cpu_bvt_set(self, op, req): - fn = FormFn(self.xd.domain_cpu_bvt_set, - [['dom', 'int'], - ['mcuadv', 'int'], - ['warp', 'int'], - ['warpl', 'int'], - ['warpu', 'int']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def op_cpu_atropos_set(self, op, req): - fn = FormFn(self.xd.domain_cpu_atropos_set, - [['dom', 'int'], - ['period', 'int'], - ['slice', 'int'], - ['latency', 'int'], - ['xtratime', 'int']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def op_vifs(self, op, req): - return self.xd.domain_vif_ls(self.dom.id) - - def op_vif(self, op, req): - fn = FormFn(self.xd.domain_vif_get, - [['dom', 'int'], - ['vif', 'int']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def op_vif_stats(self, op, req): - #todo - fn = FormFn(self.xd.domain_vif_stats, - [['dom', 'int'], - ['vif', 'int']]) - #val = fn(req.args, {'dom': self.dom.id}) - val = 999 - #return val - return val - - def op_vif_ip_add(self, op, req): - fn = FormFn(self.xd.domain_vif_ip_add, - [['dom', 'int'], - ['vif', 'int'], - ['ip', 'str']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def op_vif_scheduler_set(self, op, req): - fn = FormFn(self.xd.domain_vif_scheduler_set, - [['dom', 'int'], - ['vif', 'int'], - ['bytes', 'int'], - ['usecs', 'int']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def op_vif_scheduler_get(self, op, req): - fn = FormFn(self.xd.domain_vif_scheduler_set, - [['dom', 'int'], - ['vif', 'int']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def op_vbds(self, op, req): - return self.xd.domain_vbd_ls(self.dom.id) - - def op_vbd(self, op, req): - fn = FormFn(self.xd.domain_vbd_get, - [['dom', 'int'], - ['vbd', 'int']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def op_vbd_add(self, op, req): - fn = FormFn(self.xd.domain_vbd_add, - [['dom', 'int'], - ['uname', 'str'], - ['dev', 'str'], - ['mode', 'str']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def op_vbd_remove(self, op, req): - fn = FormFn(self.xd.domain_vbd_remove, - [['dom', 'int'], - ['dev', 'str']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - - def render_POST(self, req): - return self.perform(req) - - def render_GET(self, req): - op = req.args.get('op') - if op and op[0] in ['vifs', 'vif', 'vif_stats', 'vbds', 'vbd']: - return self.perform(req) - if self.use_sxp(req): - req.setHeader("Content-Type", sxp.mime_type) - sxp.show(self.dom.sxpr(), out=req) - else: - req.write('') - self.print_path(req) - #self.ls() - req.write('

%s

' % self.dom) - if self.dom.console: - cinfo = self.dom.console - cid = cinfo.id - #todo: Local xref: need to know server prefix. - req.write('

Console %s

' - % (cid, cid)) - req.write('

Connect to console

' - % cinfo.uri()) - if self.dom.config: - req.write("
")
-                PrettyPrint.prettyprint(self.dom.config, out=req)
-                req.write("
") - req.write('vif 0 stats' - % req.prePathURL()) - self.form(req) - req.write('') - return '' - - def form(self, req): - req.write('
' % req.prePathURL()) - req.write('') - req.write('') - req.write('') - req.write('') - req.write('
') - req.write('To: ') - req.write('
') diff --git a/tools/xenmgr/lib/server/SrvDomainDir.py b/tools/xenmgr/lib/server/SrvDomainDir.py deleted file mode 100644 index 67a6a9bc7f..0000000000 --- a/tools/xenmgr/lib/server/SrvDomainDir.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -from StringIO import StringIO - -from twisted.protocols import http -from twisted.web import error - -from xenmgr import sxp -from xenmgr import XendDomain - -from SrvDir import SrvDir -from SrvDomain import SrvDomain - -class SrvDomainDir(SrvDir): - """Service that manages the domain directory. - """ - - def __init__(self): - SrvDir.__init__(self) - self.xd = XendDomain.instance() - - def domain(self, x): - val = None - try: - dom = self.xd.domain_get(x) - val = SrvDomain(dom) - except KeyError, ex: - print 'SrvDomainDir>', ex - pass - return val - - def get(self, x): - v = SrvDir.get(self, x) - if v is not None: - return v - v = self.domain(x) - return v - - def op_create(self, op, req): - ok = 0 - try: - configstring = req.args.get('config')[0] - print 'config:', configstring - pin = sxp.Parser() - pin.input(configstring) - pin.input_eof() - config = pin.get_val() - ok = 1 - except Exception, ex: - print 'op_create>', ex - if not ok: - req.setResponseCode(http.BAD_REQUEST, "Invalid configuration") - return "Invalid configuration" - return error.ErrorPage(http.BAD_REQUEST, - "Invalid", - "Invalid configuration") - try: - deferred = self.xd.domain_create(config) - deferred.addCallback(self._cb_op_create, configstring, req) - return deferred - except Exception, ex: - raise - #return ['err', str(ex) ] - #req.setResponseCode(http.BAD_REQUEST, "Error creating domain") - #return str(ex) - #return error.ErrorPage(http.BAD_REQUEST, - # "Error creating domain", - # str(ex)) - - - def _cb_op_create(self, dominfo, configstring, req): - """Callback to handle deferred domain creation. - """ - dom = dominfo.id - domurl = "%s/%s" % (req.prePathURL(), dom) - req.setResponseCode(201, "created") - req.setHeader("Location", domurl) - if self.use_sxp(req): - return dominfo.sxpr() - else: - out = StringIO() - print >> out, ('

Created Domain %s

' - % (domurl, dom)) - print >> out, '

'
-            print >> out, configstring
-            print >> out, '

' - val = out.getvalue() - out.close() - return val - - def render_POST(self, req): - return self.perform(req) - - def render_GET(self, req): - if self.use_sxp(req): - req.setHeader("Content-Type", sxp.mime_type) - self.ls_domain(req, 1) - else: - req.write("") - self.print_path(req) - self.ls(req) - self.ls_domain(req) - self.form(req) - req.write("") - return '' - - def ls_domain(self, req, use_sxp=0): - url = req.prePathURL() - if not url.endswith('/'): - url += '/' - if use_sxp: - domains = self.xd.domain_ls() - sxp.show(domains, out=req) - else: - domains = self.xd.domains() - domains.sort(lambda x, y: cmp(x.id, y.id)) - req.write('
    ') - for d in domains: - req.write('
  • Domain %s' - % (url, d.id, d.id)) - req.write('name=%s' % d.name) - req.write('memory=%d'% d.memory) - req.write('
  • ') - req.write('
') - - def form(self, req): - req.write('
' - % req.prePathURL()) - req.write('') - req.write('Config
') - req.write('
') diff --git a/tools/xenmgr/lib/server/SrvEventDir.py b/tools/xenmgr/lib/server/SrvEventDir.py deleted file mode 100644 index eda56972da..0000000000 --- a/tools/xenmgr/lib/server/SrvEventDir.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -from xenmgr import sxp -from xenmgr import EventServer -from SrvDir import SrvDir - -class SrvEventDir(SrvDir): - """Event directory. - """ - - def __init__(self): - SrvDir.__init__(self) - self.eserver = EventServer.instance() - - def op_inject(self, op, req): - eventstring = req.args.get('event') - pin = sxp.Parser() - pin.input(eventstring) - pin.input_eof() - sxpr = pin.get_val() - self.eserver.inject(sxp.name(sxpr), sxpr) - if req.use_sxp: - sxp.name(sxpr) - else: - return '' + eventstring + '' - - def render_POST(self, req): - return self.perform(req) - - def form(self, req): - action = req.prePathURL() - req.write('
' - % action) - req.write('') - req.write('Event
') - req.write('
') - req.write('
' - % action) - req.write('') - req.write('Event file
') - req.write('
') diff --git a/tools/xenmgr/lib/server/SrvNode.py b/tools/xenmgr/lib/server/SrvNode.py deleted file mode 100644 index d4411d5e5d..0000000000 --- a/tools/xenmgr/lib/server/SrvNode.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -import os -from SrvDir import SrvDir -from xenmgr import sxp -from xenmgr import XendNode - -class SrvNode(SrvDir): - """Information about the node. - """ - - def __init__(self): - SrvDir.__init__(self) - self.xn = XendNode.instance() - - def op_shutdown(self, op, req): - val = self.xn.shutdown() - return val - - def op_reboot(self, op, req): - val = self.xn.reboot() - return val - - def op_cpu_rrobin_slice_set(self, op, req): - fn = FormFn(self.xn.cpu_rrobin_slice_set, - [['slice', 'int']]) - val = fn(req.args, {}) - return val - - def op_cpu_bvt_slice_set(self, op, req): - fn = FormFn(self.xn.cpu_bvt_slice_set, - [['slice', 'int']]) - val = fn(req.args, {}) - return val - - def render_POST(self, req): - return self.perform(req) - - def render_GET(self, req): - if self.use_sxp(req): - req.setHeader("Content-Type", sxp.mime_type) - sxp.show(['node'] + self.info(), out=req) - else: - req.write('') - self.print_path(req) - req.write('
    ') - for d in self.info(): - req.write('
  • %10s: %s' % (d[0], str(d[1]))) - req.write('
') - req.write('') - return '' - - def info(self): - return self.xn.info() diff --git a/tools/xenmgr/lib/server/SrvRoot.py b/tools/xenmgr/lib/server/SrvRoot.py deleted file mode 100644 index 6256d83a4b..0000000000 --- a/tools/xenmgr/lib/server/SrvRoot.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -from xenmgr import XendRoot -xroot = XendRoot.instance() -from SrvDir import SrvDir - -class SrvRoot(SrvDir): - """The root of the xend server. - """ - - """Server sub-components. Each entry is (name, class), where - 'name' is the entry name and 'class' is the name of its class. - """ - #todo Get this list from the XendRoot config. - subdirs = [ - ('node', 'SrvNode' ), - ('domain', 'SrvDomainDir' ), - ('console', 'SrvConsoleDir' ), - ('event', 'SrvEventDir' ), - ('device', 'SrvDeviceDir' ), - ('vnet', 'SrvVnetDir' ), - ] - - def __init__(self): - SrvDir.__init__(self) - for (name, klass) in self.subdirs: - self.add(name, klass) - for (name, klass) in self.subdirs: - self.get(name) - xroot.start() diff --git a/tools/xenmgr/lib/server/SrvServer.py b/tools/xenmgr/lib/server/SrvServer.py deleted file mode 100644 index d507c2002b..0000000000 --- a/tools/xenmgr/lib/server/SrvServer.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/python2 -# Copyright (C) 2004 Mike Wray - -"""Example xend HTTP and console server. - - Can be accessed from a browser or from a program. - Do 'python SrvServer.py' to run the server. - Then point a web browser at http://localhost:8000/xend and follow the links. - Most are stubs, except /domain which has a list of domains and a 'create domain' - button. - - You can also access the server from a program. - Do 'python XendClient.py' to run a few test operations. - - The data served differs depending on the client (as defined by User-Agent - and Accept in the HTTP headers). If the client is a browser, data - is returned in HTML, with interactive forms. If the client is a program, - data is returned in SXP format, with no forms. - - The server serves to the world by default. To restrict it to the local host - change 'interface' in main(). - - Mike Wray -""" -# todo Support security settings etc. in the config file. -# todo Support command-line args. - -from twisted.web import server -from twisted.web import resource -from twisted.internet import reactor - -from xenmgr import XendRoot -xroot = XendRoot.instance() - -from xenmgr import XendBridge - -from SrvRoot import SrvRoot - -def create(port=None, interface=None, bridge=0): - if port is None: port = 8000 - if interface is None: interface = '' - if bridge or xroot.rebooted: - init_bridge() - root = resource.Resource() - xend = SrvRoot() - root.putChild('xend', xend) - site = server.Site(root) - reactor.listenTCP(port, site, interface=interface) - -def init_bridge(): - XendBridge.bridge_create() - XendBridge.reconfigure() - -def main(port=None, interface=None): - create(port, interface) - reactor.run() - - -if __name__ == '__main__': - main() diff --git a/tools/xenmgr/lib/server/SrvVnetDir.py b/tools/xenmgr/lib/server/SrvVnetDir.py deleted file mode 100644 index a8a814192d..0000000000 --- a/tools/xenmgr/lib/server/SrvVnetDir.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) 2004 Mike Wray - -from SrvDir import SrvDir - -class SrvVnetDir(SrvDir): - """Vnet directory. - """ - - pass diff --git a/tools/xenmgr/lib/server/__init__.py b/tools/xenmgr/lib/server/__init__.py deleted file mode 100644 index 8b13789179..0000000000 --- a/tools/xenmgr/lib/server/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tools/xenmgr/lib/server/blkif.py b/tools/xenmgr/lib/server/blkif.py deleted file mode 100755 index fa46e3b642..0000000000 --- a/tools/xenmgr/lib/server/blkif.py +++ /dev/null @@ -1,341 +0,0 @@ -from twisted.internet import defer - -from xenmgr import sxp -from xenmgr import PrettyPrint - -import channel -import controller -from messages import * - -class BlkifControllerFactory(controller.ControllerFactory): - """Factory for creating block device interface controllers. - Also handles the 'back-end' channel to the device driver domain. - """ - - def __init__(self): - controller.ControllerFactory.__init__(self) - - self.majorTypes = [ CMSG_BLKIF_BE ] - - self.subTypes = { - CMSG_BLKIF_BE_CREATE : self.recv_be_create, - CMSG_BLKIF_BE_CONNECT : self.recv_be_connect, - CMSG_BLKIF_BE_VBD_CREATE : self.recv_be_vbd_create, - CMSG_BLKIF_BE_VBD_GROW : self.recv_be_vbd_grow, - CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed, - } - self.attached = 1 - self.registerChannel() - - def createInstance(self, dom, recreate=0): - d = self.addDeferred() - blkif = self.getInstanceByDom(dom) - if blkif: - self.callDeferred(blkif) - else: - blkif = BlkifController(self, dom) - self.addInstance(blkif) - if recreate: - self.callDeferred(blkif) - else: - blkif.send_be_create() - return d - - def getDomainDevices(self, dom): - blkif = self.getInstanceByDom(dom) - return (blkif and blkif.getDevices()) or [] - - def getDomainDevice(self, dom, vdev): - blkif = self.getInstanceByDom(dom) - return (blkif and blkif.getDevice(vdev)) or None - - def setControlDomain(self, dom, recreate=0): - if self.dom == dom: return - self.deregisterChannel() - if not recreate: - self.attached = 0 - self.dom = dom - self.registerChannel() - # - #if xend.blkif.be_port: - # xend.blkif.recovery = True - #xend.blkif.be_port = xend.main.port_from_dom(dom) - - def getControlDomain(self): - return self.dom - - def reattachDevice(self, dom, vdev): - blkif = self.getInstanceByDom(dom) - if blkif: - blkif.reattachDevice(vdev) - self.attached = self.devicesAttached() - if self.attached: - self.reattached() - - def devicesAttached(self): - """Check if all devices are attached. - """ - attached = 1 - for blkif in self.getInstances(): - if not blkif.attached: - attached = 0 - break - return attached - - def reattached(self): - for blkif in self.getInstances(): - blkif.reattached() - - def recv_be_create(self, msg, req): - #print 'recv_be_create>' - val = unpackMsg('blkif_be_create_t', msg) - blkif = self.getInstanceByDom(val['domid']) - self.callDeferred(blkif) - - def recv_be_connect(self, msg, req): - #print 'recv_be_create>' - val = unpackMsg('blkif_be_connect_t', msg) - blkif = self.getInstanceByDom(val['domid']) - if blkif: - blkif.send_fe_interface_status_changed() - else: - pass - - def recv_be_vbd_create(self, msg, req): - #print 'recv_be_vbd_create>' - val = unpackMsg('blkif_be_vbd_create_t', msg) - blkif = self.getInstanceByDom(val['domid']) - if blkif: - blkif.send_be_vbd_grow(val['vdevice']) - else: - pass - - def recv_be_vbd_grow(self, msg, req): - #print 'recv_be_vbd_grow>' - val = unpackMsg('blkif_be_vbd_grow_t', msg) - # Check status? - if self.attached: - self.callDeferred(0) - else: - self.reattachDevice(val['domid'], val['vdevice']) - - def recv_be_driver_status_changed(self, msg, req): - val = unpackMsg('blkif_be_driver_status_changed_t', msg) - status = val['status'] - if status == BLKIF_DRIVER_STATUS_UP and not self.attached: - for blkif in self.getInstances(): - blkif.detach() - -class BlkDev(controller.Dev): - """Info record for a block device. - """ - - def __init__(self, ctrl, vdev, mode, segment): - controller.Dev.__init__(self, ctrl) - self.vdev = vdev - self.mode = mode - self.device = segment['device'] - self.start_sector = segment['start_sector'] - self.nr_sectors = segment['nr_sectors'] - self.attached = 1 - - def readonly(self): - return 'w' not in self.mode - - def sxpr(self): - val = ['blkdev', ['vdev', self.vdev], ['mode', self.mode] ] - return val - - def destroy(self): - print 'BlkDev>destroy>', self.vdev - PrettyPrint.prettyprint(self.sxpr()) - self.controller.send_be_vbd_destroy(self.vdev) - -class BlkifController(controller.Controller): - """Block device interface controller. Handles all block devices - for a domain. - """ - - def __init__(self, factory, dom): - #print 'BlkifController> dom=', dom - controller.Controller.__init__(self, factory, dom) - self.devices = {} - - self.majorTypes = [ CMSG_BLKIF_FE ] - - self.subTypes = { - CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED: - self.recv_fe_driver_status_changed, - CMSG_BLKIF_FE_INTERFACE_CONNECT : - self.recv_fe_interface_connect, - } - self.attached = 1 - self.evtchn = None - self.registerChannel() - #print 'BlkifController<', 'dom=', self.dom, 'idx=', self.idx - - def sxpr(self): - val = ['blkif', ['dom', self.dom]] - if self.evtchn: - val.append(['evtchn', - self.evtchn['port1'], - self.evtchn['port2']]) - return val - - def lostChannel(self): - print 'BlkifController>lostChannel>', 'dom=', self.dom - #self.destroyDevices() - controller.Controller.lostChannel(self) - - def getDevices(self): - return self.devices.values() - - def getDevice(self, vdev): - return self.devices.get(vdev) - - def addDevice(self, vdev, mode, segment): - if vdev in self.devices: return None - dev = BlkDev(self, vdev, mode, segment) - self.devices[vdev] = dev - return dev - - def attachDevice(self, vdev, mode, segment, recreate=0): - """Attach a device to the specified interface. - """ - #print 'BlkifController>attach_device>', self.dom, vdev, mode, segment - dev = self.addDevice(vdev, mode, segment) - if not dev: return -1 - if recreate: - d = defer.Deferred() - d.callback(self) - else: - self.send_be_vbd_create(vdev) - d = self.factory.addDeferred() - return d - - def destroy(self): - print 'BlkifController>destroy> dom=', self.dom - def cb_destroy(val): - self.send_be_destroy() - d = self.factory.addDeferred() - d.addCallback(cb_destroy) - self.send_be_disconnect() - #self.destroyDevices() - - def destroyDevices(self): - for dev in self.getDevices(): - dev.destroy() - - def detach(self): - """Detach all devices, when the back-end control domain has changed. - """ - self.attached = 0 - for dev in self.devices.values(): - dev.attached = 0 - self.send_be_vbd_create(vdev) - - def reattachDevice(self, vdev): - """Reattach a device, when the back-end control domain has changed. - """ - dev = self.devices[vdev] - dev.attached = 1 - attached = 1 - for dev in self.devices.values(): - if not dev.attached: - attached = 0 - break - self.attached = attached - return self.attached - - def reattached(self): - """All devices have been reattached after the back-end control - domain has changed. - """ - msg = packMsg('blkif_fe_interface_status_changed_t', - { 'handle' : 0, - 'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED}) - self.writeRequest(msg) - - def recv_fe_driver_status_changed(self, msg, req): - msg = packMsg('blkif_fe_interface_status_changed_t', - { 'handle' : 0, - 'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED, - 'evtchn' : 0 }) - self.writeRequest(msg) - - def recv_fe_interface_connect(self, msg, req): - val = unpackMsg('blkif_fe_interface_connect_t', msg) - self.evtchn = channel.eventChannel(0, self.dom) - print 'recv_fe_interface_connect>' - PrettyPrint.prettyprint(self.sxpr()) - msg = packMsg('blkif_be_connect_t', - { 'domid' : self.dom, - 'blkif_handle' : val['handle'], - 'evtchn' : self.evtchn['port1'], - 'shmem_frame' : val['shmem_frame'] }) - self.factory.writeRequest(msg) - pass - - #def recv_fe_interface_status_changed(self, msg, req): - # (hnd, status, chan) = unpackMsg('blkif_fe_interface_status_changed_t', msg) - # print 'recv_fe_interface_status_changed>', hnd, status, chan - # pass - - def send_fe_interface_status_changed(self): - msg = packMsg('blkif_fe_interface_status_changed_t', - { 'handle' : 0, - 'status' : BLKIF_INTERFACE_STATUS_CONNECTED, - 'evtchn' : self.evtchn['port2'] }) - self.writeRequest(msg) - - def send_be_create(self): - msg = packMsg('blkif_be_create_t', - { 'domid' : self.dom, - 'blkif_handle' : 0 }) - self.factory.writeRequest(msg) - - def send_be_disconnect(self): - print '>BlkifController>send_be_disconnect>', 'dom=', self.dom - msg = packMsg('blkif_be_disconnect_t', - { 'domid' : self.dom, - 'blkif_handle' : 0 }) - self.factory.writeRequest(msg) - - def send_be_destroy(self): - print '>BlkifController>send_be_destroy>', 'dom=', self.dom - msg = packMsg('blkif_be_destroy_t', - { 'domid' : self.dom, - 'blkif_handle' : 0 }) - self.factory.writeRequest(msg) - - def send_be_vbd_create(self, vdev): - dev = self.devices[vdev] - msg = packMsg('blkif_be_vbd_create_t', - { 'domid' : self.dom, - 'blkif_handle' : 0, - 'vdevice' : dev.vdev, - 'readonly' : dev.readonly() }) - self.factory.writeRequest(msg) - - def send_be_vbd_grow(self, vdev): - dev = self.devices[vdev] - msg = packMsg('blkif_be_vbd_grow_t', - { 'domid' : self.dom, - 'blkif_handle' : 0, - 'vdevice' : dev.vdev, - 'extent.device' : dev.device, - 'extent.sector_start' : dev.start_sector, - 'extent.sector_length' : dev.nr_sectors }) - self.factory.writeRequest(msg) - - def send_be_vbd_destroy(self, vdev): - print '>BlkifController>send_be_vbd_destroy>', 'dom=', self.dom, 'vdev=', vdev - PrettyPrint.prettyprint(self.sxpr()) - dev = self.devices[vdev] - msg = packMsg('blkif_be_vbd_destroy_t', - { 'domid' : self.dom, - 'blkif_handle' : 0, - 'vdevice' : dev.vdev }) - del self.devices[vdev] - self.factory.writeRequest(msg) - diff --git a/tools/xenmgr/lib/server/channel.py b/tools/xenmgr/lib/server/channel.py deleted file mode 100755 index cf890918e5..0000000000 --- a/tools/xenmgr/lib/server/channel.py +++ /dev/null @@ -1,378 +0,0 @@ -import Xc; xc = Xc.new() -import xend.utils -from messages import msgTypeName - -VIRQ_MISDIRECT = 0 # Catch-all interrupt for unbound VIRQs. -VIRQ_TIMER = 1 # Timebase update, and/or requested timeout. -VIRQ_DEBUG = 2 # Request guest to dump debug info. -VIRQ_CONSOLE = 3 # (DOM0) bytes received on emergency console. -VIRQ_DOM_EXC = 4 # (DOM0) Exceptional event for some domain. - -def eventChannel(dom1, dom2): - return xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2) - -class ChannelFactory: - """Factory for creating channels. - Maintains a table of channels. - """ - - """ Channels indexed by index. """ - channels = {} - - def __init__(self): - """Constructor - do not use. Use the channelFactory function.""" - self.notifier = xend.utils.notifier() - - def addChannel(self, channel): - """Add a channel. - """ - idx = channel.idx - self.channels[idx] = channel - self.notifier.bind(idx) - # Try to wake it up - #self.notifier.unmask(idx) - #channel.notify() - - def getChannel(self, idx): - """Get the channel with the given index (if any). - """ - return self.channels.get(idx) - - def delChannel(self, idx): - """Remove the channel with the given index (if any). - """ - if idx in self.channels: - del self.channels[idx] - self.notifier.unbind(idx) - - def domChannel(self, dom): - """Get the channel for the given domain. - Construct if necessary. - """ - dom = int(dom) - for chan in self.channels.values(): - if not isinstance(chan, Channel): continue - if chan.dom == dom: - return chan - chan = Channel(self, dom) - self.addChannel(chan) - return chan - - def virqChannel(self, virq): - """Get the channel for the given virq. - Construct if necessary. - """ - for chan in self.channels.values(): - if not isinstance(chan, VirqChannel): continue - if chan.virq == virq: - return chan - chan = VirqChannel(self, virq) - self.addChannel(chan) - return chan - - def channelClosed(self, channel): - """The given channel has been closed - remove it. - """ - self.delChannel(channel.idx) - - def createPort(self, dom): - """Create a port for a channel to the given domain. - """ - return xend.utils.port(dom) - -def channelFactory(): - """Singleton constructor for the channel factory. - Use this instead of the class constructor. - """ - global inst - try: - inst - except: - inst = ChannelFactory() - return inst - -class BaseChannel: - """Abstract superclass for channels. - - The subclass constructor must set idx to the port to use. - """ - - def __init__(self, factory): - self.factory = factory - self.idx = -1 - self.closed = 0 - - def getIndex(self): - """Get the channel index. - """ - return self.idx - - def notificationReceived(self, type): - """Called when a notification is received. - Closes the channel on error, otherwise calls - handleNotification(type), which should be defined - in a subclass. - """ - #print 'notificationReceived> type=', type, self - if self.closed: return - if type == self.factory.notifier.EXCEPTION: - print 'notificationReceived> EXCEPTION' - info = xc.evtchn_status(self.idx) - if info['status'] == 'unbound': - print 'notificationReceived> EXCEPTION closing...' - self.close() - return - self.handleNotification(type) - - def close(self): - """Close the channel. Calls channelClosed() on the factory. - Override in subclass. - """ - self.factory.channelClosed(self) - - def handleNotification(self, type): - """Handle notification. - Define in subclass. - """ - pass - - -class VirqChannel(BaseChannel): - """A channel for handling a virq. - """ - - def __init__(self, factory, virq): - """Create a channel for the given virq using the given factory. - - Do not call directly, use virqChannel on the factory. - """ - BaseChannel.__init__(self, factory) - self.virq = virq - # Notification port (int). - self.port = xc.evtchn_bind_virq(virq) - self.idx = self.port - # Clients to call when a virq arrives. - self.clients = [] - - def __repr__(self): - return ('' - % (self.virq, self.port)) - - def getVirq(self): - """Get the channel's virq. - """ - return self.virq - - def close(self): - """Close the channel. Calls lostChannel(self) on all its clients and - channelClosed() on the factory. - """ - for c in self.clients: - c.lostChannel(self) - del self.clients - BaseChannel.close(self) - - def registerClient(self, client): - """Register a client. The client will be called with - client.virqReceived(virq) when a virq is received. - The client will be called with client.lostChannel(self) if the - channel is closed. - """ - self.clients.append(client) - - def handleNotification(self, type): - for c in self.clients: - c.virqReceived(self.virq) - - def notify(self): - xc.evtchn_send(self.port) - - -class Channel(BaseChannel): - """A control channel to a domain. Messages for the domain device controllers - are multiplexed over the channel (console, block devs, net devs). - """ - - def __init__(self, factory, dom): - """Create a channel to the given domain using the given factory. - - Do not call directly, use domChannel on the factory. - """ - BaseChannel.__init__(self, factory) - # Domain. - self.dom = int(dom) - # Domain port (object). - self.port = self.factory.createPort(dom) - # Channel port (int). - self.idx = self.port.local_port - # Registered devices. - self.devs = [] - # Devices indexed by the message types they handle. - self.devs_by_type = {} - # Output queue. - self.queue = [] - self.closed = 0 - - def getLocalPort(self): - """Get the local port. - """ - return self.port.local_port - - def getRemotePort(self): - """Get the remote port. - """ - return self.port.remote_port - - def close(self): - """Close the channel. Calls lostChannel() on all its devices and - channelClosed() on the factory. - """ - self.closed = 1 - for d in self.devs: - d.lostChannel() - self.factory.channelClosed(self) - self.devs = [] - self.devs_by_type = {} - - def registerDevice(self, types, dev): - """Register a device controller. - - @param types message types the controller handles - @param dev device controller - """ - if self.closed: return - self.devs.append(dev) - for ty in types: - self.devs_by_type[ty] = dev - - def deregisterDevice(self, dev): - """Remove the registration for a device controller. - - @param dev device controller - """ - if dev in self.devs: - self.devs.remove(dev) - types = [ ty for (ty, d) in self.devs_by_type.items() if d == dev ] - for ty in types: - del self.devs_by_type[ty] - - def getDevice(self, type): - """Get the device controller handling a message type. - - @param type message type - @returns controller or None - """ - return self.devs_by_type.get(type) - - def getMessageType(self, msg): - """Get a 2-tuple of the message type and subtype. - """ - hdr = msg.get_header() - return (hdr['type'], hdr.get('subtype')) - - def __repr__(self): - return ('' - % (self.dom, - self.port.local_port, - self.port.remote_port)) - - def handleNotification(self, type): - work = 0 - work += self.handleRequests() - work += self.handleResponses() - work += self.handleWrites() - if work: - self.notify() - - def notify(self): - self.port.notify() - - def handleRequests(self): - work = 0 - while 1: - msg = self.readRequest() - if not msg: break - self.requestReceived(msg) - work += 1 - return work - - def requestReceived(self, msg): - (ty, subty) = self.getMessageType(msg) - #todo: Must respond before writing any more messages. - #todo: Should automate this (respond on write) - self.port.write_response(msg) - dev = self.getDevice(ty) - if dev: - dev.requestReceived(msg, ty, subty) - else: - print ("requestReceived> No device: Message type %s %d:%d" - % (msgTypeName(ty, subty), ty, subty)), self - - def handleResponses(self): - work = 0 - while 1: - msg = self.readResponse() - if not msg: break - self.responseReceived(msg) - work += 1 - return work - - def responseReceived(self, msg): - (ty, subty) = self.getMessageType(msg) - dev = self.getDevice(ty) - if dev: - dev.responseReceived(msg, ty, subty) - else: - print ("responseReceived> No device: Message type %d:%d" - % (msgTypeName(ty, subty), ty, subty)), self - - def handleWrites(self): - work = 0 - # Pull data from producers. - for dev in self.devs: - work += dev.produceRequests() - # Flush the queue. - while self.queue and self.port.space_to_write_request(): - msg = self.queue.pop(0) - self.port.write_request(msg) - work += 1 - return work - - def writeRequest(self, msg, notify=1): - if self.closed: - val = -1 - elif self.writeReady(): - self.port.write_request(msg) - if notify: self.notify() - val = 1 - else: - self.queue.append(msg) - val = 0 - return val - - def writeResponse(self, msg): - if self.closed: return -1 - self.port.write_response(msg) - return 1 - - def writeReady(self): - if self.closed or self.queue: return 0 - return self.port.space_to_write_request() - - def readRequest(self): - if self.closed: - return None - if self.port.request_to_read(): - val = self.port.read_request() - else: - val = None - return val - - def readResponse(self): - if self.closed: - return None - if self.port.response_to_read(): - val = self.port.read_response() - else: - val = None - return val diff --git a/tools/xenmgr/lib/server/console.py b/tools/xenmgr/lib/server/console.py deleted file mode 100755 index f26a4dd3c6..0000000000 --- a/tools/xenmgr/lib/server/console.py +++ /dev/null @@ -1,230 +0,0 @@ - -from twisted.internet import reactor -from twisted.internet import protocol -from twisted.protocols import telnet - -import xend.utils - -from xenmgr import EventServer -eserver = EventServer.instance() - -import controller -from messages import * -from params import * - -"""Telnet binary option.""" -TRANSMIT_BINARY = '0' -WILL = chr(251) -IAC = chr(255) - -class ConsoleProtocol(protocol.Protocol): - """Asynchronous handler for a console TCP socket. - """ - - def __init__(self, controller, idx): - self.controller = controller - self.idx = idx - self.addr = None - self.binary = 0 - - def connectionMade(self): - peer = self.transport.getPeer() - self.addr = (peer.host, peer.port) - if self.controller.connect(self.addr, self): - self.transport.write("Cannot connect to console %d on domain %d\n" - % (self.idx, self.controller.dom)) - self.loseConnection() - return - else: - self.transport.write("Connected to console %d on domain %d\n" - % (self.idx, self.controller.dom)) - self.setTelnetTransmitBinary() - eserver.inject('xend.console.connect', - [self.idx, self.addr[0], self.addr[1]]) - - def setTelnetTransmitBinary(self): - """Send the sequence to set the telnet TRANSMIT-BINARY option. - """ - self.write(IAC + WILL + TRANSMIT_BINARY) - - def dataReceived(self, data): - if self.controller.handleInput(self, data): - self.loseConnection() - - def write(self, data): - #if not self.connected: return -1 - self.transport.write(data) - return len(data) - - def connectionLost(self, reason=None): - eserver.inject('xend.console.disconnect', - [self.idx, self.addr[0], self.addr[1]]) - self.controller.disconnect() - - def loseConnection(self): - self.transport.loseConnection() - -class ConsoleFactory(protocol.ServerFactory): - """Asynchronous handler for a console server socket. - """ - protocol = ConsoleProtocol - - def __init__(self, controller, idx): - #protocol.ServerFactory.__init__(self) - self.controller = controller - self.idx = idx - - def buildProtocol(self, addr): - proto = self.protocol(self.controller, self.idx) - proto.factory = self - return proto - -class ConsoleControllerFactory(controller.ControllerFactory): - """Factory for creating console controllers. - """ - - def createInstance(self, dom, console_port=None): - if console_port is None: - console_port = CONSOLE_PORT_BASE + dom - console = ConsoleController(self, dom, console_port) - self.addInstance(console) - eserver.inject('xend.console.create', - [console.idx, console.dom, console.console_port]) - return console - - def consoleClosed(self, console): - eserver.inject('xend.console.close', console.idx) - self.delInstance(console) - -class ConsoleController(controller.Controller): - """Console controller for a domain. - Does not poll for i/o itself, but relies on the notifier to post console - output and the connected TCP sockets to post console input. - """ - - def __init__(self, factory, dom, console_port): - #print 'ConsoleController> dom=', dom, type(dom) - controller.Controller.__init__(self, factory, dom) - self.majorTypes = [ CMSG_CONSOLE ] - self.status = "new" - self.addr = None - self.conn = None - self.rbuf = xend.utils.buffer() - self.wbuf = xend.utils.buffer() - self.console_port = console_port - - self.registerChannel() - self.listener = None - self.listen() - #print 'ConsoleController<', 'dom=', self.dom, 'idx=', self.idx - - def sxpr(self): - val =['console', - ['status', self.status ], - ['id', self.idx ], - ['domain', self.dom ], - ['local_port', self.channel.getLocalPort() ], - ['remote_port', self.channel.getRemotePort() ], - ['console_port', self.console_port ] ] - if self.addr: - val.append(['connected', self.addr[0], self.addr[1]]) - return val - - def ready(self): - return not (self.closed() or self.rbuf.empty()) - - def closed(self): - return self.status == 'closed' - - def connected(self): - return self.status == 'connected' - - def close(self): - try: - #print 'ConsoleController> close dom=', self.dom - self.status = "closed" - if self.conn: - self.conn.loseConnection() - self.listener.stopListening() - self.deregisterChannel() - self.lostChannel() - except Exception, ex: - print 'ConsoleController>close>', ex - raise - - def listen(self): - """Listen for TCP connections to the console port.. - """ - if self.closed(): return - self.status = "listening" - if self.listener: - #self.listener.startListening() - pass - else: - f = ConsoleFactory(self, self.idx) - self.listener = reactor.listenTCP(self.console_port, f) - - def connect(self, addr, conn): - if self.closed(): return -1 - if self.connected(): return -1 - self.addr = addr - self.conn = conn - self.status = "connected" - self.handleOutput() - return 0 - - def disconnect(self): - if self.conn: - self.conn.loseConnection() - self.addr = None - self.conn = None - self.listen() - - def requestReceived(self, msg, type, subtype): - #print '***Console', self.dom, msg.get_payload() - self.rbuf.write(msg.get_payload()) - self.handleOutput() - - def responseReceived(self, msg, type, subtype): - pass - - def produceRequests(self): - # Send as much pending console data as there is room for. - work = 0 - while not self.wbuf.empty() and self.channel.writeReady(): - msg = xend.utils.message(CMSG_CONSOLE, 0, 0) - msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD)) - work += self.channel.writeRequest(msg, notify=0) - return work - - def handleInput(self, conn, data): - """Handle some external input aimed at the console. - Called from a TCP connection (conn). - """ - if self.closed(): return -1 - if conn != self.conn: return 0 - self.wbuf.write(data) - if self.produceRequests(): - self.channel.notify() - return 0 - - def handleOutput(self): - """Handle buffered output from the console. - Sends it to the connected console (if any). - """ - if self.closed(): - #print 'Console>handleOutput> closed' - return -1 - if not self.conn: - #print 'Console>handleOutput> not connected' - return 0 - while not self.rbuf.empty(): - try: - #print 'Console>handleOutput> writing...' - bytes = self.conn.write(self.rbuf.peek()) - if bytes > 0: - self.rbuf.discard(bytes) - except socket.error, error: - pass - #print 'Console>handleOutput<' - return 0 diff --git a/tools/xenmgr/lib/server/controller.py b/tools/xenmgr/lib/server/controller.py deleted file mode 100755 index 900c2d55b0..0000000000 --- a/tools/xenmgr/lib/server/controller.py +++ /dev/null @@ -1,169 +0,0 @@ -from twisted.internet import defer - -import channel -from messages import msgTypeName - -class CtrlMsgRcvr: - """Abstract class for things that deal with a control interface to a domain. - """ - - - def __init__(self): - self.channelFactory = channel.channelFactory() - self.majorTypes = [ ] - self.subTypes = {} - self.dom = None - self.channel = None - self.idx = None - - def requestReceived(self, msg, type, subtype): - method = self.subTypes.get(subtype) - if method: - method(msg, 1) - else: - print ('requestReceived> No handler: Message type %s %d:%d' - % (msgTypeName(type, subtype), type, subtype)), self - - def responseReceived(self, msg, type, subtype): - method = self.subTypes.get(subtype) - if method: - method(msg, 0) - else: - print ('responseReceived> No handler: Message type %s %d:%d' - % (msgTypeName(type, subtype), type, subtype)), self - - def lostChannel(self): - pass - - def registerChannel(self): - #print 'CtrlMsgRcvr>registerChannel>', self - self.channel = self.channelFactory.domChannel(self.dom) - self.idx = self.channel.getIndex() - if self.majorTypes: - self.channel.registerDevice(self.majorTypes, self) - - def deregisterChannel(self): - #print 'CtrlMsgRcvr>deregisterChannel>', self - if self.channel: - self.channel.deregisterDevice(self) - del self.channel - - def produceRequests(self): - return 0 - - def writeRequest(self, msg): - if self.channel: - self.channel.writeRequest(msg) - else: - print 'CtrlMsgRcvr>writeRequest>', 'no channel!', self - - def writeResponse(self, msg): - if self.channel: - self.channel.writeResponse(msg) - else: - print 'CtrlMsgRcvr>writeResponse>', 'no channel!', self - -class ControllerFactory(CtrlMsgRcvr): - """Abstract class for factories creating controllers. - Maintains a table of instances. - """ - - def __init__(self): - CtrlMsgRcvr.__init__(self) - self.instances = {} - self.dlist = [] - self.dom = 0 - # Timeout (in seconds) for deferreds. - self.timeout = 10 - - def addInstance(self, instance): - self.instances[instance.idx] = instance - - def getInstance(self, idx): - return self.instances.get(idx) - - def getInstances(self): - return self.instances.values() - - def getInstanceByDom(self, dom): - for inst in self.instances.values(): - if inst.dom == dom: - return inst - return None - - def delInstance(self, instance): - #print 'ControllerFactory>delInstance>', instance.idx - if instance.idx in self.instances: - #print 'ControllerFactory>delInstance> remove', instance.idx - del self.instances[instance.idx] - - def createInstance(self, dom, recreate=0): - raise NotImplementedError() - - def instanceClosed(self, instance): - #print 'ControllerFactory>instanceClosed>', instance.idx, instance - self.delInstance(instance) - - def addDeferred(self): - d = defer.Deferred() - if self.timeout > 0: - # The deferred will error if not called before timeout. - d.setTimeout(self.timeout) - self.dlist.append(d) - return d - - def callDeferred(self, *args): - if self.dlist: - d = self.dlist.pop(0) - d.callback(*args) - - def errDeferred(self, *args): - if self.dlist: - d = self.dlist.pop(0) - d.errback(*args) - -class Controller(CtrlMsgRcvr): - """Abstract class for a device controller attached to a domain. - """ - - def __init__(self, factory, dom): - CtrlMsgRcvr.__init__(self) - self.factory = factory - self.dom = int(dom) - self.channel = None - self.idx = None - - def close(self): - self.deregisterChannel() - self.lostChannel() - - def lostChannel(self): - #print 'Controller>lostChannel>', self, self.factory - self.factory.instanceClosed(self) - -class Dev: - - def __init__(self, controller): - self.controller = controller - self.props = {} - - def setprop(self, k, v): - self.props[k] = v - - def getprop(self, k, v=None): - return self.props.get(k, v) - - def hasprop(self, k): - return k in self.props - - def delprop(self, k): - if k in self.props: - del self.props[k] - - #def __repr__(self): - # return str(self.sxpr()) - - def sxpr(self): - raise NotImplementedError() - - diff --git a/tools/xenmgr/lib/server/cstruct.py b/tools/xenmgr/lib/server/cstruct.py deleted file mode 100755 index 880931b41f..0000000000 --- a/tools/xenmgr/lib/server/cstruct.py +++ /dev/null @@ -1,269 +0,0 @@ -import struct - -class Struct: - - maxDepth = 10 - - base = ['x', 'B', 'H', 'I', 'L', 'Q', 'c', 'h', 'i', 'l', 'q', ] - - sizes = {'B': 1, - 'H': 2, - 'I': 4, - 'L': 4, - 'Q': 8, - 'c': 1, - 'h': 2, - 'i': 4, - 'l': 4, - 'q': 8, - 'x': 1, - } - - formats = { - 'int8' : 'B', - 'int16' : 'H', - 'int32' : 'I', - 'int64' : 'Q', - 'u8' : 'B', - 'u16' : 'H', - 'u32' : 'I', - 'u64' : 'Q' - } - - def typedef(self, name, val): - self.formats[name] = val - - def struct(self, name, *f): - self.typedef(name, StructInfo(self, f)) - - def getType(self, name): - return self.formats[name] - - def format(self, ty): - d = 0 - f = ty - while d < self.maxDepth: - d += 1 - f = self.formats[f] - if isinstance(f, StructInfo): - return f.format() - if f in self.base: - return f - return -1 - - def alignedformat(self, ty): - fmt = self.format(ty) - #print 'alignedformat> %s |%s|' %(ty, fmt) - afmt = self.align(fmt) - #print 'alignedformat< %s |%s| |%s|' % (ty, fmt, afmt) - return afmt - - def align(self, fmt): - n1 = 0 - afmt = '' - for a in fmt: - n2 = self.getSize(a) - m = n1 % n2 - if m: - d = (n2 - m) - afmt += 'x' * d - n1 += d - afmt += a - n1 += n2 - return afmt - - def fmtsize(self, fmt): - s = 0 - for f in fmt: - s += self.getSize(f) - return s - - def getSize(self, f): - return self.sizes[f] - - def pack(self, ty, data): - return self.getType(ty).pack(data) - - def unpack(self, ty, data): - return self.getType(ty).unpack(data) - - def show(self): - l = self.formats.keys() - l.sort() - for v in l: - print "%-35s %-10s %s" % (v, self.format(v), self.alignedformat(v)) - - -class StructInfo: - - def __init__(self, s, f): - self.fmt = None - self.structs = s - self.fields = f - - def alignedformat(self): - if self.afmt: return self.afmt - self.afmt = self.structs.align(self.format()) - return self.afmt - - def format(self): - if self.fmt: return self.fmt - fmt = "" - for (ty, name) in self.fields: - fmt += self.formatString(ty) - self.fmt = fmt - return fmt - - def formatString(self, ty): - if ty in self.fields: - ty = self.fields[ty] - return self.structs.format(ty) - - def pack(self, *args): - return struct.pack(self.alignedformat(), *args) - - def unpack(self, data): - return struct.unpack(self.alignedformat(), data) - -types = Struct() - -types.typedef('short' , 'h') -types.typedef('int' , 'i') -types.typedef('long' , 'l') -types.typedef('unsigned short', 'H') -types.typedef('unsigned int' , 'I') -types.typedef('unsigned long' , 'L') -types.typedef('domid_t' , 'u64') -types.typedef('blkif_vdev_t' , 'u16') -types.typedef('blkif_pdev_t' , 'u16') -types.typedef('blkif_sector_t', 'u64') - -types.struct('u8[6]', - ('u8', 'a1'), - ('u8', 'a2'), - ('u8', 'a3'), - ('u8', 'a4'), - ('u8', 'a5'), - ('u8', 'a6')) - -types.struct('blkif_fe_interface_status_changed_t', - ('unsigned int', 'handle'), - ('unsigned int', 'status'), - ('unsigned int', 'evtchn')) - -types.struct('blkif_fe_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('blkif_fe_interface_connect_t', - ('unsigned int' , 'handle'), - ('unsigned long', 'shmem_frame')) - -types.struct('blkif_fe_interface_disconnect_t', - ('unsigned int', 'handle')) - -types.struct('blkif_extent_t', - ('blkif_pdev_t' , 'device'), - ('blkif_sector_t', 'sector_start'), - ('blkif_sector_t', 'sector_length')) - -types.struct('blkif_be_create_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_connect_t', - ('domid_t' , 'domid'), - ('unsigned int' , 'blkif_handle'), - ('unsigned int' , 'evtchn'), - ('unsigned long', 'shmem_frame'), - ('unsigned int' , 'status')) - -types.struct('blkif_be_disconnect_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_vbd_create_t', - ('domid_t' , 'domid'), #Q - ('unsigned int', 'blkif_handle'), #I - ('blkif_vdev_t', 'vdevice'), #H - ('int' , 'readonly'), #i - ('unsigned int', 'status')) #I - -types.struct('blkif_be_vbd_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('blkif_vdev_t', 'vdevice'), - ('unsigned int', 'status')) - -types.struct('blkif_be_vbd_grow_t', - ('domid_t' , 'domid'), #Q - ('unsigned int' , 'blkif_handle'), #I - ('blkif_vdev_t' , 'vdevice'), #H - ('blkif_extent_t', 'extent'), #HQQ - ('unsigned int' , 'status')) #I - -types.struct('blkif_be_vbd_shrink_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('blkif_vdev_t', 'vdevice'), - ('unsigned int', 'status')) - -types.struct('blkif_be_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('netif_fe_interface_status_changed_t', - ('unsigned int', 'handle'), - ('unsigned int', 'status'), - ('unsigned int', 'evtchn'), - ('u8[6]', 'mac')) - -types.struct('netif_fe_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('netif_fe_interface_connect_t', - ('unsigned int', 'handle'), - ('unsigned long', 'tx_shmem_frame'), - ('unsigned long', 'rx_shmem_frame')) - -types.struct('netif_fe_interface_disconnect_t', - ('unsigned int', 'handle')) - -types.struct('netif_be_create_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('u8[6]' , 'mac'), - ('unsigned int', 'status')) - -types.struct('netif_be_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('unsigned int', 'status')) - -types.struct('netif_be_connect_t', - ('domid_t' , 'domid'), - ('unsigned int' , 'netif_handle'), - ('unsigned int' , 'evtchn'), - ('unsigned long', 'tx_shmem_frame'), - ('unsigned long', 'rx_shmem_frame'), - ('unsigned int' , 'status')) - -types.struct('netif_be_disconnect_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('unsigned int', 'status')) - -types.struct('netif_be_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -if 1 or __name__ == "__main__": - types.show() diff --git a/tools/xenmgr/lib/server/domain.py b/tools/xenmgr/lib/server/domain.py deleted file mode 100644 index ab22234480..0000000000 --- a/tools/xenmgr/lib/server/domain.py +++ /dev/null @@ -1,41 +0,0 @@ -import channel -import controller -from messages import * - -class DomainControllerFactory(controller.ControllerFactory): - """Factory for creating domain controllers. - """ - - def createInstance(self, dom): - d = DomainController(self, dom) - self.addInstance(d) - return d - - def getInstanceByDom(self, dom): - for inst in self.instances.values(): - if inst.dom == dom: - return inst - inst = self.createInstance(dom) - return inst - - -class DomainController(controller.Controller): - """Generic controller for a domain. - """ - - reasons = {'poweroff' : 'shutdown_poweroff_t', - 'reboot' : 'shutdown_reboot_t', - 'suspend' : 'shutdown_suspend_t' } - - def __init__(self, factory, dom): - controller.Controller.__init__(self, factory, dom) - self.majorTypes = [ CMSG_SHUTDOWN ] - self.registerChannel() - print 'DomainController>', self, self.channel, self.idx - - def shutdown(self, reason): - msgtype = self.reasons.get(reason) - if not msgtype: - raise ValueError('invalid reason:' + reason) - msg = packMsg(msgtype, {}) - self.writeRequest(msg) diff --git a/tools/xenmgr/lib/server/messages.py b/tools/xenmgr/lib/server/messages.py deleted file mode 100644 index f920bd679e..0000000000 --- a/tools/xenmgr/lib/server/messages.py +++ /dev/null @@ -1,219 +0,0 @@ -import struct - -import xend.utils - -DEBUG = 0 - -""" All message formats. -Added to incrementally for the various message types. -See below. -""" -msg_formats = {} - -#============================================================================ -# Console message types. -#============================================================================ - -CMSG_CONSOLE = 0 - -console_formats = { 'console_data': (CMSG_CONSOLE, 0) } - -msg_formats.update(console_formats) - -#============================================================================ -# Block interface message types. -#============================================================================ - -CMSG_BLKIF_BE = 1 -CMSG_BLKIF_FE = 2 - -CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED = 0 -CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED = 32 -CMSG_BLKIF_FE_INTERFACE_CONNECT = 33 -CMSG_BLKIF_FE_INTERFACE_DISCONNECT = 34 - -CMSG_BLKIF_BE_CREATE = 0 -CMSG_BLKIF_BE_DESTROY = 1 -CMSG_BLKIF_BE_CONNECT = 2 -CMSG_BLKIF_BE_DISCONNECT = 3 -CMSG_BLKIF_BE_VBD_CREATE = 4 -CMSG_BLKIF_BE_VBD_DESTROY = 5 -CMSG_BLKIF_BE_VBD_GROW = 6 -CMSG_BLKIF_BE_VBD_SHRINK = 7 -CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED = 32 - -BLKIF_DRIVER_STATUS_DOWN = 0 -BLKIF_DRIVER_STATUS_UP = 1 - -BLKIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */ -BLKIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */ -BLKIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */ - -BLKIF_BE_STATUS_OKAY = 0 -BLKIF_BE_STATUS_ERROR = 1 -BLKIF_BE_STATUS_INTERFACE_EXISTS = 2 -BLKIF_BE_STATUS_INTERFACE_NOT_FOUND = 3 -BLKIF_BE_STATUS_INTERFACE_CONNECTED = 4 -BLKIF_BE_STATUS_VBD_EXISTS = 5 -BLKIF_BE_STATUS_VBD_NOT_FOUND = 6 -BLKIF_BE_STATUS_OUT_OF_MEMORY = 7 -BLKIF_BE_STATUS_EXTENT_NOT_FOUND = 8 -BLKIF_BE_STATUS_MAPPING_ERROR = 9 - -blkif_formats = { - 'blkif_be_connect_t': - (CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT), - - 'blkif_be_create_t': - (CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE), - - 'blkif_be_disconnect_t': - (CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT), - - 'blkif_be_destroy_t': - (CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY), - - 'blkif_be_vbd_create_t': - (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE), - - 'blkif_be_vbd_grow_t': - (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW), - - 'blkif_be_vbd_destroy_t': - (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY), - - 'blkif_fe_interface_status_changed_t': - (CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED), - - 'blkif_fe_driver_status_changed_t': - (CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED), - - 'blkif_fe_interface_connect_t': - (CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT), -} - -msg_formats.update(blkif_formats) - -#============================================================================ -# Network interface message types. -#============================================================================ - -CMSG_NETIF_BE = 3 -CMSG_NETIF_FE = 4 - -CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED = 0 -CMSG_NETIF_FE_DRIVER_STATUS_CHANGED = 32 -CMSG_NETIF_FE_INTERFACE_CONNECT = 33 -CMSG_NETIF_FE_INTERFACE_DISCONNECT = 34 - -CMSG_NETIF_BE_CREATE = 0 -CMSG_NETIF_BE_DESTROY = 1 -CMSG_NETIF_BE_CONNECT = 2 -CMSG_NETIF_BE_DISCONNECT = 3 -CMSG_NETIF_BE_DRIVER_STATUS_CHANGED = 32 - -NETIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */ -NETIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */ -NETIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */ - -NETIF_DRIVER_STATUS_DOWN = 0 -NETIF_DRIVER_STATUS_UP = 1 - -netif_formats = { - 'netif_be_connect_t': - (CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT), - - 'netif_be_create_t': - (CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE), - - 'netif_be_disconnect_t': - (CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT), - - 'netif_be_destroy_t': - (CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY), - - 'netif_be_driver_status_changed_t': - (CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED), - - 'netif_fe_driver_status_changed_t': - (CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED), - - 'netif_fe_interface_connect_t': - (CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT), - - 'netif_fe_interface_status_changed_t': - (CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED), - } - -msg_formats.update(netif_formats) - -#============================================================================ -CMSG_SHUTDOWN = 6 - -CMSG_SHUTDOWN_POWEROFF = 0 -CMSG_SHUTDOWN_REBOOT = 1 -CMSG_SHUTDOWN_SUSPEND = 2 - -STOPCODE_shutdown = 0 -STOPCODE_reboot = 1 -STOPCODE_suspend = 2 - -shutdown_formats = { - 'shutdown_poweroff_t': - (CMSG_SHUTDOWN, CMSG_SHUTDOWN_POWEROFF), - - 'shutdown_reboot_t': - (CMSG_SHUTDOWN, CMSG_SHUTDOWN_REBOOT), - - 'shutdown_suspend_t': - (CMSG_SHUTDOWN, CMSG_SHUTDOWN_SUSPEND), - } - -msg_formats.update(shutdown_formats) - -#============================================================================ - -class Msg: - pass - -def packMsg(ty, params): - if DEBUG: print '>packMsg', ty, params - (major, minor) = msg_formats[ty] - args = {} - for (k, v) in params.items(): - if k == 'mac': - for i in range(0, 6): - args['mac[%d]' % i] = v[i] - else: - args[k] = v - if DEBUG: - for (k, v) in args.items(): - print 'packMsg>', k, v, type(v) - msgid = 0 - msg = xend.utils.message(major, minor, msgid, args) - return msg - -def unpackMsg(ty, msg): - args = msg.get_payload() - mac = [0, 0, 0, 0, 0, 0] - macs = [] - for (k, v) in args.items(): - if k.startswith('mac['): - macs += k - i = int(k[4:5]) - mac[i] = v - else: - pass - if macs: - args['mac'] = mac - for k in macs: - del args[k] - if DEBUG: print 'createInstance> dom=', dom - netif = self.getInstanceByDom(dom) - if netif is None: - netif = NetifController(self, dom) - self.addInstance(netif) - return netif - - def getDomainDevices(self, dom): - netif = self.getInstanceByDom(dom) - return (netif and netif.getDevices()) or [] - - def getDomainDevice(self, dom, vif): - netif = self.getInstanceByDom(dom) - return (netif and netif.getDevice(vif)) or None - - def setControlDomain(self, dom, recreate=0): - """Set the 'back-end' device driver domain. - """ - if self.dom == dom: return - self.deregisterChannel() - if not recreate: - self.attached = 0 - self.dom = dom - self.registerChannel() - # - #if xend.netif.be_port.remote_dom != 0: - # xend.netif.recovery = True - # xend.netif.be_port = xend.main.port_from_dom(dom) - # - - def getControlDomain(self): - return self.dom - - def recv_be_create(self, msg, req): - self.callDeferred(0) - - def recv_be_connect(self, msg, req): - val = unpackMsg('netif_be_connect_t', msg) - dom = val['domid'] - vif = val['netif_handle'] - netif = self.getInstanceByDom(dom) - if netif: - netif.send_interface_connected(vif) - else: - print "recv_be_connect> unknown vif=", vif - pass - - def recv_be_driver_status_changed(self, msg, req): - val = unpackMsg('netif_be_driver_status_changed_t', msg) - status = val['status'] - if status == NETIF_DRIVER_STATUS_UP and not self.attached: - # If we are not attached the driver domain was changed, and - # this signals the new driver domain is ready. - for netif in self.getInstances(): - netif.reattach_devices() - self.attached = 1 - -## pl = msg.get_payload() -## status = pl['status'] -## if status == NETIF_DRIVER_STATUS_UP: -## if xend.netif.recovery: -## print "New netif backend now UP, notifying guests:" -## for netif_key in interface.list.keys(): -## netif = interface.list[netif_key] -## netif.create() -## print " Notifying %d" % netif.dom -## msg = xend.utils.message( -## CMSG_NETIF_FE, -## CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0, -## { 'handle' : 0, 'status' : 1 }) -## netif.ctrlif_tx_req(xend.main.port_from_dom(netif.dom),msg) -## print "Done notifying guests" -## recovery = False - -class NetDev(controller.Dev): - """Info record for a network device. - """ - - def __init__(self, ctrl, vif, mac): - controller.Dev.__init__(self, ctrl) - self.vif = vif - self.mac = mac - self.evtchn = None - self.bridge = None - - def sxpr(self): - vif = str(self.vif) - mac = ':'.join(map(lambda x: "%x" % x, self.mac)) - val = ['netdev', ['vif', vif], ['mac', mac]] - if self.bridge: - val.append(['bridge', self.bridge]) - if self.evtchn: - val.append(['evtchn', - self.evtchn['port1'], - self.evtchn['port2']]) - return val - - def bridge_add(self, bridge): - self.bridge = XendBridge.vif_bridge_add(self.controller.dom, self.vif, bridge) - - def bridge_rem(self): - if not self.bridge: return - XendBridge.vif_bridge_rem(self.controller.dom, self.vif, self.bridge) - self.bridge = None - - def destroy(self): - def cb_destroy(val): - self.controller.send_be_destroy(self.vif) - print 'NetDev>destroy>', 'vif=', self.vif - PrettyPrint.prettyprint(self.sxpr()) - self.bridge_rem() - d = self.controller.factory.addDeferred() - d.addCallback(cb_destroy) - self.controller.send_be_disconnect(self.vif) - #self.controller.send_be_destroy(self.vif) - - -class NetifController(controller.Controller): - """Network interface controller. Handles all network devices for a domain. - """ - - def __init__(self, factory, dom): - #print 'NetifController> dom=', dom - controller.Controller.__init__(self, factory, dom) - self.devices = {} - - self.majorTypes = [ CMSG_NETIF_FE ] - - self.subTypes = { - CMSG_NETIF_FE_DRIVER_STATUS_CHANGED: - self.recv_fe_driver_status_changed, - CMSG_NETIF_FE_INTERFACE_CONNECT : - self.recv_fe_interface_connect, - } - self.registerChannel() - #print 'NetifController<', 'dom=', self.dom, 'idx=', self.idx - - def sxpr(self): - val = ['netif', ['dom', self.dom]] - return val - - def randomMAC(self): - # VIFs get a random MAC address with a "special" vendor id. - # - # NB. The vendor is currently an "obsolete" one that used to belong - # to DEC (AA-00-00). Using it is probably a bit rude :-) - # - # NB2. The first bit of the first random octet is set to zero for - # all dynamic MAC addresses. This may allow us to manually specify - # MAC addresses for some VIFs with no fear of clashes. - mac = [ 0xaa, 0x00, 0x00, - random.randint(0x00, 0x7f), - random.randint(0x00, 0xff), - random.randint(0x00, 0xff) ] - return mac - - def lostChannel(self): - print 'NetifController>lostChannel>', 'dom=', self.dom - #self.destroyDevices() - controller.Controller.lostChannel(self) - - def getDevices(self): - return self.devices.values() - - def getDevice(self, vif): - return self.devices.get(vif) - - def addDevice(self, vif, vmac): - if vmac is None: - mac = self.randomMAC() - else: - mac = [ int(x, 16) for x in vmac.split(':') ] - if len(mac) != 6: raise ValueError("invalid mac") - #print "attach_device>", "vif=", vif, "mac=", mac - dev = NetDev(self, vif, mac) - self.devices[vif] = dev - return dev - - def destroy(self): - print 'NetifController>destroy>', 'dom=', self.dom - self.destroyDevices() - - def destroyDevices(self): - for dev in self.getDevices(): - dev.destroy() - - def attachDevice(self, vif, vmac, recreate=0): - """Attach a network device. - If vmac is None a random mac address is assigned. - - @param vif interface index - @param vmac mac address (string) - """ - self.addDevice(vif, vmac) - if recreate: - d = defer.Deferred() - d.callback(self) - else: - d = self.factory.addDeferred() - self.send_be_create(vif) - return d - - def reattach_devices(self): - """Reattach all devices when the back-end control domain has changed. - """ - d = self.factory.addDeferred() - self.send_be_create(vif) - self.attach_fe_devices(0) - - def attach_fe_devices(self): - for dev in self.devices.values(): - msg = packMsg('netif_fe_interface_status_changed_t', - { 'handle' : dev.vif, - 'status' : NETIF_INTERFACE_STATUS_DISCONNECTED, - 'evtchn' : 0, - 'mac' : dev.mac }) - self.writeRequest(msg) - - def recv_fe_driver_status_changed(self, msg, req): - if not req: return - msg = packMsg('netif_fe_driver_status_changed_t', - { 'status' : NETIF_DRIVER_STATUS_UP, - 'nr_interfaces' : len(self.devices) }) - self.writeRequest(msg) - self.attach_fe_devices() - - def recv_fe_interface_connect(self, msg, req): - val = unpackMsg('netif_fe_interface_connect_t', msg) - dev = self.devices[val['handle']] - dev.evtchn = channel.eventChannel(0, self.dom) - msg = packMsg('netif_be_connect_t', - { 'domid' : self.dom, - 'netif_handle' : dev.vif, - 'evtchn' : dev.evtchn['port1'], - 'tx_shmem_frame' : val['tx_shmem_frame'], - 'rx_shmem_frame' : val['rx_shmem_frame'] }) - self.factory.writeRequest(msg) - - def send_interface_connected(self, vif): - dev = self.devices[vif] - msg = packMsg('netif_fe_interface_status_changed_t', - { 'handle' : dev.vif, - 'status' : NETIF_INTERFACE_STATUS_CONNECTED, - 'evtchn' : dev.evtchn['port2'], - 'mac' : dev.mac }) - self.writeRequest(msg) - - def send_be_create(self, vif): - dev = self.devices[vif] - msg = packMsg('netif_be_create_t', - { 'domid' : self.dom, - 'netif_handle' : dev.vif, - 'mac' : dev.mac }) - self.factory.writeRequest(msg) - - def send_be_disconnect(self, vif): - dev = self.devices[vif] - msg = packMsg('netif_be_disconnect_t', - { 'domid' : self.dom, - 'netif_handle' : dev.vif }) - self.factory.writeRequest(msg) - - def send_be_destroy(self, vif): - print 'NetifController>send_be_destroy>', 'dom=', self.dom, 'vif=', vif - PrettyPrint.prettyprint(self.sxpr()) - dev = self.devices[vif] - del self.devices[vif] - msg = packMsg('netif_be_destroy_t', - { 'domid' : self.dom, - 'netif_handle' : vif }) - self.factory.writeRequest(msg) diff --git a/tools/xenmgr/lib/server/params.py b/tools/xenmgr/lib/server/params.py deleted file mode 100644 index d8f064cf0c..0000000000 --- a/tools/xenmgr/lib/server/params.py +++ /dev/null @@ -1,10 +0,0 @@ -# The following parameters could be placed in a configuration file. -PID_FILE = '/var/run/xend.pid' -LOG_FILE = '/var/log/xend.log' -USER = 'root' -CONTROL_DIR = '/var/run/xend' -MGMT_SOCK = 'xenmgrsock' # relative to CONTROL_DIR -EVENT_PORT = 8001 - -CONSOLE_PORT_BASE = 9600 - diff --git a/tools/xenmgr/lib/sxp.py b/tools/xenmgr/lib/sxp.py deleted file mode 100644 index dd4fece6f0..0000000000 --- a/tools/xenmgr/lib/sxp.py +++ /dev/null @@ -1,557 +0,0 @@ -#!/usr/bin/python2 -# Copyright (C) 2004 Mike Wray -""" -Input-driven parsing for s-expression (sxp) format. -Create a parser: pin = Parser(); -Then call pin.input(buf) with your input. -Call pin.input_eof() when done. -Use pin.read() to see if a value has been parsed, pin.get_val() -to get a parsed value. You can call ready and get_val at any time - -you don't have to wait until after calling input_eof. - -""" -from __future__ import generators - -import sys -import types -import errno -import string - -__all__ = [ - "mime_type", - "ParseError", - "Parser", - "atomp", - "show", - "show_xml", - "elementp", - "name", - "attributes", - "attribute", - "children", - "child", - "child_at", - "child0", - "child1", - "child2", - "child3", - "child4", - "child_value", - "has_id", - "with_id", - "child_with_id", - "elements", - "parse", - ] - -mime_type = "application/sxp" - -escapes = { - 'a': '\a', - 'b': '\b', - 't': '\t', - 'n': '\n', - 'v': '\v', - 'f': '\f', - 'r': '\r', - '\\': '\\', - '\'': '\'', - '\"': '\"'} - -k_list_open = "(" -k_list_close = ")" -k_attr_open = "@" -k_eval = "!" - -escapes_rev = {} -for k in escapes: - escapes_rev[escapes[k]] = k - -class ParseError(StandardError): - - def __init__(self, parser, value): - self.parser = parser - self.value = value - - def __str__(self): - return self.value - -class ParserState: - - def __init__(self, fn, parent=None): - self.parent = parent - self.buf = '' - self.val = [] - self.delim = None - self.fn = fn - - def push(self, fn): - return ParserState(fn, parent=self) - -class Parser: - - def __init__(self): - self.error = sys.stderr - self.reset() - - def reset(self): - self.val = [] - self.eof = 0 - self.err = 0 - self.line_no = 0 - self.char_no = 0 - self.state = None - - def push_state(self, fn): - self.state = self.state.push(fn) - - def pop_state(self): - val = self.state - self.state = self.state.parent - if self.state and self.state.fn == self.state_start: - # Return to start state - produce the value. - self.val += self.state.val - self.state.val = [] - return val - - def in_class(self, c, s): - return s.find(c) >= 0 - - def in_space_class(self, c): - return self.in_class(c, ' \t\n\v\f\r') - - def is_separator(self, c): - return self.in_class(c, '{}()<>[]!;') - - def in_comment_class(self, c): - return self.in_class(c, '#') - - def in_string_quote_class(self, c): - return self.in_class(c, '"\'') - - def in_printable_class(self, c): - return self.in_class(c, string.printable) - - def set_error_stream(self, error): - self.error = error - - def has_error(self): - return self.err > 0 - - def at_eof(self): - return self.eof - - def input_eof(self): - self.eof = 1 - self.input_char(-1) - - def input(self, buf): - if not buf or len(buf) == 0: - self.input_eof() - else: - for c in buf: - self.input_char(c) - - def input_char(self, c): - if self.at_eof(): - pass - elif c == '\n': - self.line_no += 1 - self.char_no = 0 - else: - self.char_no += 1 - - if self.state is None: - self.begin_start(None) - self.state.fn(c) - - def ready(self): - return len(self.val) > 0 - - def get_val(self): - v = self.val[0] - self.val = self.val[1:] - return v - - def get_all(self): - return self.val - - def begin_start(self, c): - self.state = ParserState(self.state_start) - - def end_start(self): - self.val += self.state.val - self.pop_state() - - def state_start(self, c): - if self.at_eof(): - self.end_start() - elif self.in_space_class(c): - pass - elif self.in_comment_class(c): - self.begin_comment(c) - elif c == k_list_open: - self.begin_list(c) - elif c == k_list_close: - raise ParseError(self, "syntax error: "+c) - elif self.in_string_quote_class(c): - self.begin_string(c) - elif self.in_printable_class(c): - self.begin_atom(c) - elif c == chr(4): - # ctrl-D, EOT: end-of-text. - self.input_eof() - else: - raise ParseError(self, "invalid character: code %d" % ord(c)) - - def begin_comment(self, c): - self.push_state(self.state_comment) - self.state.buf += c - - def end_comment(self): - self.pop_state() - - def state_comment(self, c): - if c == '\n' or self.at_eof(): - self.end_comment() - else: - self.state.buf += c - - def begin_string(self, c): - self.push_state(self.state_string) - self.state.delim = c - - def end_string(self): - val = self.state.buf - self.state.parent.val.append(val) - self.pop_state() - - def state_string(self, c): - if self.at_eof(): - raise ParseError(self, "unexpected EOF") - elif c == self.state.delim: - self.end_string() - elif c == '\\': - self.push_state(self.state_escape) - else: - self.state.buf += c - - def state_escape(self, c): - if self.at_eof(): - raise ParseError(self, "unexpected EOF") - d = escapes.get(c) - if d: - self.state.parent.buf += d - self.pop_state() - elif c == 'x': - self.state.fn = self.state_hex - self.state.val = 0 - else: - self.state.fn = self.state_octal - self.state.val = 0 - self.input_char(c) - - def state_octal(self, c): - def octaldigit(c): - self.state.val *= 8 - self.state.val += ord(c) - ord('0') - self.state.buf += c - if self.state.val < 0 or self.state.val > 0xff: - raise ParseError(self, "invalid octal escape: out of range " + self.state.buf) - if len(self.state.buf) == 3: - octaldone() - - def octaldone(): - d = chr(self.state.val) - self.state.parent.buf += d - self.pop_state() - - if self.at_eof(): - raise ParseError(self, "unexpected EOF") - elif '0' <= c <= '7': - octaldigit(c) - elif len(self.buf): - octaldone() - self.input_char(c) - - def state_hex(self, c): - def hexdone(): - d = chr(self.state.val) - self.state.parent.buf += d - self.pop_state() - - def hexdigit(c, d): - self.state.val *= 16 - self.state.val += ord(c) - ord(d) - self.state.buf += c - if self.state.val < 0 or self.state.val > 0xff: - raise ParseError(self, "invalid hex escape: out of range " + self.state.buf) - if len(self.state.buf) == 2: - hexdone() - - if self.at_eof(): - raise ParseError(self, "unexpected EOF") - elif '0' <= c <= '9': - hexdigit(c, '0') - elif 'A' <= c <= 'F': - hexdigit(c, 'A') - elif 'a' <= c <= 'f': - hexdigit(c, 'a') - elif len(buf): - hexdone() - self.input_char(c) - - def begin_atom(self, c): - self.push_state(self.state_atom) - self.state.buf = c - - def end_atom(self): - val = self.state.buf - self.state.parent.val.append(val) - self.pop_state() - - def state_atom(self, c): - if self.at_eof(): - self.end_atom() - elif (self.is_separator(c) or - self.in_space_class(c) or - self.in_comment_class(c)): - self.end_atom() - self.input_char(c) - else: - self.state.buf += c - - def begin_list(self, c): - self.push_state(self.state_list) - - def end_list(self): - val = self.state.val - self.state.parent.val.append(val) - self.pop_state() - - def state_list(self, c): - if self.at_eof(): - raise ParseError(self, "unexpected EOF") - elif c == k_list_close: - self.end_list() - else: - self.state_start(c) - -def atomp(sxpr): - if sxpr.isalnum() or sxpr == '@': - return 1 - for c in sxpr: - if c in string.whitespace: return 0 - if c in '"\'\\(){}[]<>$#&%^': return 0 - if c in string.ascii_letters: continue - if c in string.digits: continue - if c in '.-_:/~': continue - return 0 - return 1 - -def show(sxpr, out=sys.stdout): - if isinstance(sxpr, types.ListType): - out.write(k_list_open) - i = 0 - for x in sxpr: - if i: out.write(' ') - show(x, out) - i += 1 - out.write(k_list_close) - elif isinstance(sxpr, types.StringType) and atomp(sxpr): - out.write(sxpr) - else: - #out.write("'" + str(sxpr) + "'") - out.write(repr(str(sxpr))) - -def show_xml(sxpr, out=sys.stdout): - if isinstance(sxpr, types.ListType): - element = name(sxpr) - out.write('<%s' % element) - for attr in attributes(sxpr): - out.write(' %s=%s' % (attr[0], attr[1])) - out.write('>') - i = 0 - for x in children(sxpr): - if i: out.write(' ') - show_xml(x, out) - i += 1 - out.write('' % element) - elif isinstance(sxpr, types.StringType) and atomp(sxpr): - out.write(sxpr) - else: - out.write(str(sxpr)) - -def elementp(sxpr, elt=None): - return (isinstance(sxpr, types.ListType) - and len(sxpr) - and (None == elt or sxpr[0] == elt)) - -def name(sxpr): - val = None - if isinstance(sxpr, types.StringType): - val = sxpr - elif isinstance(sxpr, types.ListType) and len(sxpr): - val = sxpr[0] - return val - -def attributes(sxpr): - val = [] - if isinstance(sxpr, types.ListType) and len(sxpr) > 1: - attr = sxpr[1] - if elementp(attr, k_attr_open): - val = attr[1:] - return val - -def attribute(sxpr, key, val=None): - for x in attributes(sxpr): - if x[0] == key: - val = x[1] - break - return val - -def children(sxpr, elt=None): - val = [] - if isinstance(sxpr, types.ListType) and len(sxpr) > 1: - i = 1 - x = sxpr[i] - if elementp(x, k_attr_open): - i += 1 - val = sxpr[i : ] - if elt: - def iselt(x): - return elementp(x, elt) - val = filter(iselt, val) - return val - -def child(sxpr, elt, val=None): - for x in children(sxpr): - if elementp(x, elt): - val = x - break - return val - -def child_at(sxpr, index, val=None): - kids = children(sxpr) - if len(kids) > index: - val = kids[index] - return val - -def child0(sxpr, val=None): - return child_at(sxpr, 0, val) - -def child1(sxpr, val=None): - return child_at(sxpr, 1, val) - -def child2(sxpr, val=None): - return child_at(sxpr, 2, val) - -def child3(sxpr, val=None): - return child_at(sxpr, 3, val) - -def child4(sxpr, val=None): - return child_at(sxpr, 4, val) - -def child_value(sxpr, elt, val=None): - kid = child(sxpr, elt) - if kid: - val = child_at(kid, 0, val) - return val - -def has_id(sxpr, id): - """Test if an s-expression has a given id. - """ - return attribute(sxpr, 'id') == id - -def with_id(sxpr, id, val=None): - """Find the first s-expression with a given id, at any depth. - - sxpr s-exp or list - id id - val value if not found (default None) - - return s-exp or val - """ - if isinstance(sxpr, types.ListType): - for n in sxpr: - if has_id(n, id): - val = n - break - v = with_id(n, id) - if v is None: continue - val = v - break - return val - -def child_with_id(sxpr, id, val=None): - """Find the first child with a given id. - - sxpr s-exp or list - id id - val value if not found (default None) - - return s-exp or val - """ - if isinstance(sxpr, types.ListType): - for n in sxpr: - if has_id(n, id): - val = n - break - return val - -def elements(sxpr, ctxt=None): - """Generate elements (at any depth). - Visit elements in pre-order. - Values generated are (node, context) - The context is None if there is no parent, otherwise - (index, parent, context) where index is the node's index w.r.t its parent, - and context is the parent's context. - - sxpr s-exp - - returns generator - """ - yield (sxpr, ctxt) - i = 0 - for n in children(sxpr): - if isinstance(n, types.ListType): - # Calling elements() recursively does not generate recursively, - # it just returns a generator object. So we must iterate over it. - for v in elements(n, (i, sxpr, ctxt)): - yield v - i += 1 - -def parse(io): - """Completely parse all input from 'io'. - - io input file object - returns list of values, None if incomplete - raises ParseError on parse error - """ - pin = Parser() - while 1: - buf = io.readline() - pin.input(buf) - if len(buf) == 0: - break - if pin.ready(): - val = pin.get_all() - else: - val = None - return val - - -if __name__ == '__main__': - print ">main" - pin = Parser() - while 1: - buf = sys.stdin.read(1024) - #buf = sys.stdin.readline() - pin.input(buf) - while pin.ready(): - val = pin.get_val() - print - print '****** val=', val - if len(buf) == 0: - break - diff --git a/tools/xenmgr/lib/xm/__init__.py b/tools/xenmgr/lib/xm/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tools/xenmgr/lib/xm/create.py b/tools/xenmgr/lib/xm/create.py deleted file mode 100644 index d0bfa0859c..0000000000 --- a/tools/xenmgr/lib/xm/create.py +++ /dev/null @@ -1,366 +0,0 @@ -# Copyright (C) 2004 Mike Wray -"""Domain creation. -""" -import string -import sys - -from xenmgr import sxp -from xenmgr import PrettyPrint -from xenmgr.XendClient import server - -from xenmgr.xm.opts import * - -gopts = Opts(use="""[options] - -Create a domain. -""") - -gopts.opt('help', short='h', - fn=set_true, default=0, - use="Print this help.") - -gopts.opt('quiet', short='q', - fn=set_true, default=0, - use="Quiet.") - -gopts.opt('path', val='PATH', - fn=set_value, default='.:/etc/xen', - use="Search path for default scripts.") - -gopts.opt('defaults', short='f', val='FILE', - fn=set_value, default='xmdefaults', - use="Use the given default script.") - -gopts.opt('config', short='F', val='FILE', - fn=set_value, default=None, - use='Domain configuration to use (SXP).') - -gopts.opt('load', short='L', val='FILE', - fn=set_value, default=None, - use='Domain saved state to load.') - -gopts.opt('define', short='D', val='VAR=VAL', - fn=set_var, default=None, - use="""Set a variable before loading defaults, e.g. '-D vmid=3' - to set vmid. May be repeated to set more thanone variable.""") - -gopts.opt('dryrun', short='n', - fn=set_true, default=0, - use="Dry run - print the config but don't create the domain.") - -gopts.opt('name', short='N', val='NAME', - fn=set_value, default=None, - use="Domain name.") - -gopts.opt('console', short='c', - fn=set_true, default=0, - use="Connect to console after domain is created.") - -gopts.opt('kernel', short='k', val='FILE', - fn=set_value, default=None, - use="Path to kernel image.") - -gopts.opt('ramdisk', short='r', val='FILE', - fn=set_value, default='', - use="Path to ramdisk.") - -gopts.opt('builder', short='b', val='FUNCTION', - fn=set_value, default='linux', - use="Function to use to build the domain.") - -gopts.opt('memory', short='m', val='MEMORY', - fn=set_value, default=128, - use="Domain memory in MB.") - -gopts.opt('blkif', - fn=set_true, default=0, - use="Make the domain a block device backend.") - -gopts.opt('netif', - fn=set_true, default=0, - use="Make the domain a network interface backend.") - -gopts.opt('disk', short='d', val='phy:DEV,VDEV,MODE', - fn=append_value, default=[], - use="""Add a disk device to a domain. The physical device is DEV, which - is exported to the domain as VDEV. The disk is read-only if MODE - is 'r', read-write if MODE is 'w'. - The option may be repeated to add more than one disk. - """) - -gopts.opt('pci', val='BUS,DEV,FUNC', - fn=append_value, default=[], - use="""Add a PCI device to a domain, using given params (in hex). - For example '-pci c0,02,1a'. - The option may be repeated to add more than one pci device. - """) - -gopts.opt('ipaddr', short='i', val="IPADDR", - fn=append_value, default=[], - use="Add an IP address to the domain.") - -gopts.opt('vif', val="mac=MAC,bridge=BRIDGE", - fn=append_value, default=[], - use="""Add a network interface with the given MAC address and bridge. - If mac is not specified a random MAC address is used. - If bridge is not specified the default bridge is used. - This option may be repeated to add more than one vif. - Specifying vifs will increase the number of interfaces as needed. - """) - -gopts.opt('nics', val="NUM", - fn=set_int, default=1, - use="""Set the number of network interfaces. - Use the vif option to define interface parameters, otherwise - defaults are used. Specifying vifs will increase the - number of interfaces as needed. - """) - -gopts.opt('root', short='R', val='DEVICE', - fn=set_value, default='', - use="""Set the root= parameter on the kernel command line. - Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""") - -gopts.opt('extra', short='E', val="ARGS", - fn=set_value, default='', - use="Set extra arguments to append to the kernel command line.") - -gopts.opt('ip', short='I', val='IPADDR', - fn=set_value, default='', - use="Set the kernel IP interface address.") - -gopts.opt('gateway', val="IPADDR", - fn=set_value, default='', - use="Set the kernel IP gateway.") - -gopts.opt('netmask', val="MASK", - fn=set_value, default = '', - use="Set the kernel IP netmask.") - -gopts.opt('hostname', val="NAME", - fn=set_value, default='', - use="Set the kernel IP hostname.") - -gopts.opt('interface', val="INTF", - fn=set_value, default="eth0", - use="Set the kernel IP interface name.") - -gopts.opt('dhcp', val="off|dhcp", - fn=set_value, default='off', - use="Set the kernel dhcp option.") - -gopts.opt('nfs_server', val="IPADDR", - fn=set_value, default=None, - use="Set the address of the NFS server for NFS root.") - -gopts.opt('nfs_root', val="PATH", - fn=set_value, default=None, - use="Set the path of the root NFS directory.") - -def strip(pre, s): - """Strip prefix 'pre' if present. - """ - if s.startswith(pre): - return s[len(pre):] - else: - return s - -def configure_image(config, opts): - """Create the image config. - """ - config_image = [ opts.builder ] - config_image.append([ 'kernel', os.path.abspath(opts.kernel) ]) - if opts.ramdisk: - config_image.append([ 'ramdisk', os.path.abspath(opts.ramdisk) ]) - if opts.cmdline_ip: - cmdline_ip = strip('ip=', opts.cmdline_ip) - config_image.append(['ip', cmdline_ip]) - if opts.root: - cmdline_root = strip('root=', opts.root) - config_image.append(['root', cmdline_root]) - if opts.extra: - config_image.append(['args', opts.extra]) - config.append(['image', config_image ]) - -def configure_disks(config_devs, opts): - """Create the config for disks (virtual block devices). - """ - for (uname, dev, mode) in opts.disk: - config_vbd = ['vbd', - ['uname', uname], - ['dev', dev ], - ['mode', mode ] ] - config_devs.append(['device', config_vbd]) - -def configure_pci(config_devs, opts): - """Create the config for pci devices. - """ - for (bus, dev, func) in opts.pci: - config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]] - config_devs.append(['device', config_pci]) - -def configure_vifs(config_devs, opts): - """Create the config for virtual network interfaces. - """ - vifs = opts.vif - vifs_n = max(opts.nics, len(vifs)) - - for idx in range(0, vifs_n): - if idx < len(vifs): - d = vifs[idx] - mac = d.get('mac') - bridge = d.get('bridge') - else: - mac = None - bridge = None - config_vif = ['vif'] - if mac: - config_vif.append(['mac', mac]) - if bridge: - config_vif.append(['bridge', bridge]) - config_devs.append(['device', config_vif]) - -def configure_vfr(config, opts): - if not opts.ipaddr: return - config_vfr = ['vfr'] - idx = 0 # No way of saying which IP is for which vif? - for ip in opts.ipaddr: - config_vfr.append(['vif', ['id', idx], ['ip', ip]]) - config.append(config_vfr) - - -def make_config(opts): - """Create the domain configuration. - """ - - config = ['vm', - ['name', opts.name ], - ['memory', opts.memory ] ] - if opts.cpu: - config.append(['cpu', opts.cpu]) - if opts.blkif: - config.append(['backend', ['blkif']]) - if opts.netif: - config.append(['backend', ['netif']]) - - configure_image(config, opts) - config_devs = [] - configure_disks(config_devs, opts) - configure_pci(config_devs, opts) - configure_vifs(config_devs, opts) - config += config_devs - return config - -def preprocess_disk(opts): - if not opts.disk: return - disk = [] - for v in opts.disk: - d = v.split(',') - if len(d) != 3: - opts.err('Invalid disk specifier: ' + v) - disk.append(d) - opts.disk = disk - -def preprocess_pci(opts): - if not opts.pci: return - pci = [] - for v in opts.pci: - d = v.split(',') - if len(d) != 3: - opts.err('Invalid pci specifier: ' + v) - # Components are in hex: add hex specifier. - hexd = map(lambda v: '0x'+v, d) - pci.append(hexd) - opts.pci = pci - -def preprocess_vifs(opts): - if not opts.vif: return - vifs = [] - for vif in opts.vif: - d = {} - a = vif.split(',') - for b in a: - (k, v) = b.strip().split('=') - k = k.strip() - v = v.strip() - if k not in ['mac', 'bridge']: - opts.err('Invalid vif specifier: ' + vif) - d[k] = v - vifs.append(d) - opts.vif = vifs - -def preprocess_ip(opts): - setip = (opts.hostname or opts.netmask - or opts.gateway or opts.dhcp or opts.interface) - if not setip: return - #if not opts - ip = (opts.ip - + ':' - + ':' + opts.gateway - + ':' + opts.netmask - + ':' + opts.hostname - + ':' + opts.interface - + ':' + opts.dhcp) - opts.cmdline_ip = ip - -def preprocess_nfs(opts): - if (opts.nfs_root or opts.nfs_server): - if (not opts.nfs_root) or (not opts.nfs_server): - opts.err('Must set nfs root and nfs server') - else: - return - nfs = 'nfsroot=' + opts.nfs_server + ':' + opts.nfs_root - opts.extra = nfs + ' ' + opts.extra - -def preprocess(opts): - if not opts.kernel: - opts.err("No kernel specified") - preprocess_disk(opts) - preprocess_pci(opts) - preprocess_vifs(opts) - preprocess_ip(opts) - preprocess_nfs(opts) - -def make_domain(opts, config): - """Create, build and start a domain. - Returns: [int] the ID of the new domain. - """ - if opts.vals.load: - filename = os.path.abspath(opts.vals.load) - dominfo = server.xend_domain_restore(filename, config) - else: - dominfo = server.xend_domain_create(config) - - dom = int(sxp.child_value(dominfo, 'id')) - console_info = sxp.child(dominfo, 'console') - if console_info: - console_port = int(sxp.child_value(console_info, 'port')) - else: - console_port = None - - if server.xend_domain_unpause(dom) < 0: - server.xend_domain_destroy(dom) - opts.err("Failed to start domain %d" % dom) - opts.info("Started domain %d, console on port %d" - % (dom, console_port)) - return (dom, console_port) - -def main(argv): - opts = gopts - args = opts.parse(argv) - if opts.vals.help: - opts.usage() - return - if opts.vals.config: - pass - else: - opts.load_defaults() - preprocess(opts.vals) - config = make_config(opts.vals) - if opts.vals.dryrun: - PrettyPrint.prettyprint(config) - else: - make_domain(opts, config) - -if __name__ == '__main__': - main(sys.argv) diff --git a/tools/xenmgr/lib/xm/main.py b/tools/xenmgr/lib/xm/main.py deleted file mode 100644 index 673cccd9fa..0000000000 --- a/tools/xenmgr/lib/xm/main.py +++ /dev/null @@ -1,448 +0,0 @@ -# Copyright (C) 2004 Mike Wray -"""Grand unified management application for Xen. -""" -import os -import os.path -import sys -from getopt import getopt - -from xenmgr import PrettyPrint -from xenmgr import sxp -from xenmgr.XendClient import server -from xenmgr.xm import create, shutdown - -class Prog: - """Base class for sub-programs. - """ - - """Program group it belongs to""" - group = 'all' - """Program name.""" - name = '??' - """Short program info.""" - info = '' - - def __init__(self, xm): - self.xm = xm - - def err(self, msg): - self.xm.err(msg) - - def help(self, args): - self.shortHelp(args) - - def shortHelp(self, args): - print "%-14s %s" % (self.name, self.info) - - def main(self, args): - """Program main entry point. - """ - pass - - -class ProgUnknown(Prog): - - name = 'unknown' - info = '' - - def help(self, args): - self.xm.err("Unknown command: %s\nTry '%s help' for more information." - % (args[0], self.xm.name)) - - main = help - -class Xm: - """Main application. - """ - - def __init__(self): - self.name = 'xm' - self.unknown = ProgUnknown(self) - self.progs = {} - - def err(self, msg): - print >>sys.stderr, "Error:", msg - sys.exit(1) - - def main(self, args): - """Main entry point. Dispatches to the progs. - """ - self.name = args[0] - if len(args) < 2: - self.err("Missing command\nTry '%s help' for more information." - % self.name) - help = self.helparg(args) - p = self.getprog(args[1], self.unknown) - if help: - p.help(args[1:]) - else: - p.main(args[1:]) - - def helparg(self, args): - for a in args: - if a in ['-h', '--help']: - return 1 - return 0 - - def prog(self, pklass): - """Add a sub-program. - - pklass program class (Prog subclass) - """ - p = pklass(self) - self.progs[p.name] = p - return p - - def getprog(self, name, val=None): - """Get a sub-program. - """ - return self.progs.get(name, val) - - def proglist(self): - """Get a list of sub-programs, ordered by group. - """ - groups = {} - for p in self.progs.values(): - l = groups.get(p.group, []) - l.append(p) - groups[p.group] = l - kl = groups.keys() - kl.sort() - pl = [] - for k in kl: - l = groups[k] - l.sort() - pl += l - return pl - -# Create the application object, then add the sub-program classes. -xm = Xm() - -class ProgHelp(Prog): - - name = "help" - info = "Print help." - - def help(self, args): - if len(args) == 2: - name = args[1] - p = self.xm.getprog(name) - if p: - p.help(args[1:]) - else: - print '%s: Unknown command: %s' % (self.name, name) - else: - for p in self.xm.proglist(): - p.shortHelp(args) - print "\nTry '%s help CMD' for help on CMD" % self.xm.name - - main = help - -xm.prog(ProgHelp) - -class ProgCreate(Prog): - - group = 'domain' - name = "create" - info = """Create a domain.""" - - def help(self, args): - create.main([args[0], '-h']) - - def main(self, args): - create.main(args) - -xm.prog(ProgCreate) - -class ProgSave(Prog): - group = 'domain' - name = "save" - info = """Save domain state (and config) to file.""" - - def help(self, args): - print args[0], "DOM FILE" - print """\nSave domain with id DOM to FILE.""" - - def main(self, args): - if len(args) < 3: self.err("%s: Missing arguments" % args[0]) - dom = args[1] - savefile = os.path.abspath(args[2]) - server.xend_domain_save(dom, savefile) - -xm.prog(ProgSave) - -class ProgRestore(Prog): - group = 'domain' - name = "restore" - info = """Create a domain from a saved state.""" - - def help(self, args): - print args[0], "FILE [CONFIG]" - print "\nRestore a domain from FILE using configuration CONFIG." - - def main(self, help, args): - if len(args) < 2: self.err("%s: Missing arguments" % args[0]) - savefile = os.path.abspath(args[1]) - if len(args) >= 3: - configfile = os.path.abspath(args[2]) - else: - configfile = None - info = server.xend_domain_restore(savefile, configfile) - PrettyPrint.prettyprint(info) - -xm.prog(ProgRestore) - -class ProgList(Prog): - group = 'domain' - name = "list" - info = """List info about domains.""" - - short_options = 'l' - long_options = ['long'] - - def help(self, args): - if help: - print args[0], '[options] [DOM...]' - print """\nGet information about domains. - Either all domains or the domains given. - - -l, --long Get more detailed information. - """ - return - - def main(self, args): - use_long = 0 - (options, params) = getopt(args[1:], - self.short_options, - self.long_options) - n = len(params) - for (k, v) in options: - if k in ['-l', '--long']: - use_long = 1 - - if n == 0: - doms = map(int, server.xend_domains()) - doms.sort() - else: - doms = map(int, params) - - if use_long: - self.long_list(doms) - else: - self.brief_list(doms) - - def brief_list(self, doms): - print 'Dom Name Mem(MB) CPU State Time(s)' - for dom in doms: - info = server.xend_domain(dom) - d = {} - d['dom'] = int(dom) - d['name'] = sxp.child_value(info, 'name', '??') - d['mem'] = int(sxp.child_value(info, 'memory', '0')) - d['cpu'] = int(sxp.child_value(info, 'cpu', '0')) - d['state'] = sxp.child_value(info, 'state', '??') - d['cpu_time'] = float(sxp.child_value(info, 'cpu_time', '0')) - print ("%(dom)-4d %(name)-16s %(mem)7d %(cpu)3d %(state)5s %(cpu_time)7.1f" % d) - - def long_list(self, doms): - for dom in doms: - info = server.xend_domain(dom) - print '\nDomain %d' % dom - PrettyPrint.prettyprint(info) - -xm.prog(ProgList) - -class ProgDestroy(Prog): - group = 'domain' - name = "destroy" - info = """Terminate a domain immediately.""" - - def help(self, args): - print args[0], 'DOM' - print '\nTerminate domain DOM immediately.' - - def main(self, args): - if len(args) < 2: self.err("%s: Missing domain" % args[0]) - dom = args[1] - server.xend_domain_destroy(dom) - -xm.prog(ProgDestroy) - -class ProgShutdown(Prog): - group = 'domain' - name = "shutdown" - info = """Shutdown a domain.""" - - def help(self, args): - shutdown.main([args[0], '-h']) - - def main(self, args): - shutdown.main(args) - -xm.prog(ProgShutdown) - -class ProgPause(Prog): - group = 'domain' - name = "pause" - info = """Pause execution of a domain.""" - - def help(self, args): - print args[0], 'DOM' - print '\nPause execution of domain DOM.' - - def main(self, args): - if len(args) < 2: self.err("%s: Missing domain" % args[0]) - dom = args[1] - server.xend_domain_pause(dom) - -xm.prog(ProgPause) - -class ProgUnpause(Prog): - group = 'domain' - name = "unpause" - info = """Unpause a paused domain.""" - - def help(self, args): - print args[0], 'DOM' - print '\nUnpause execution of domain DOM.' - - def main(self, args): - if len(args) < 2: self.err("%s: Missing domain" % args[0]) - dom = args[1] - server.xend_domain_unpause(dom) - -xm.prog(ProgUnpause) - -class ProgPincpu(Prog): - group = 'domain' - name = "pincpu" - info = """Pin a domain to a cpu. """ - - def help(self, args): - print args[0],'DOM CPU' - print '\nPin domain DOM to cpu CPU.' - - def main(self, args): - if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0]) - v = map(int, args[1:3]) - server.xend_domain_pincpu(*v) - -xm.prog(ProgPincpu) - -class ProgBvt(Prog): - group = 'scheduler' - name = "bvt" - info = """Set BVT scheduler parameters.""" - - def help(self, args): - print args[0], "DOM MCUADV WARP WARPL WARPU" - print '\nSet Borrowed Virtual Time scheduler parameters.' - - def main(self, args): - if len(args) != 6: self.err("%s: Invalid argument(s)" % args[0]) - v = map(int, args[1:6]) - server.xend_domain_cpu_bvt_set(*v) - -xm.prog(ProgBvt) - -class ProgBvtslice(Prog): - group = 'scheduler' - name = "bvtslice" - info = """Set the BVT scheduler slice.""" - - def help(self, args): - print args[0], 'SLICE' - print '\nSet Borrowed Virtual Time scheduler slice.' - - def main(self, args): - if len(args) < 2: self.err('%s: Missing slice' % args[0]) - server.xend_node_cpu_bvt_slice_set(slice) - -xm.prog(ProgBvtslice) - -class ProgAtropos(Prog): - group = 'scheduler' - name= "atropos" - info = """Set atropos parameters.""" - - def help(self, args): - print args[0], "DOM PERIOD SLICE LATENCY XTRATIME" - print "\nSet atropos parameters." - - def main(self, args): - if len(args) != 5: self.err("%s: Invalid argument(s)" % args[0]) - v = map(int, args[1:5]) - server.xend_domain_cpu_atropos_set(*v) - -xm.prog(ProgAtropos) - -class ProgRrobin(Prog): - group = 'scheduler' - name = "rrobin" - info = """Set round robin slice.""" - - def help(self, args): - print args[0], "SLICE" - print "\nSet round robin scheduler slice." - - def main(self, args): - if len(args) != 2: self.err("%s: Invalid argument(s)" % args[0]) - rrslice = int(args[1]) - server.xend_node_rrobin_set(rrslice) - -xm.prog(ProgRrobin) - -class ProgInfo(Prog): - group = 'host' - name = "info" - info = """Get information about the xen host.""" - - def main(self, args): - info = server.xend_node() - for x in info[1:]: - print "%-23s:" % x[0], x[1] - -xm.prog(ProgInfo) - -class ProgConsoles(Prog): - group = 'console' - name = "consoles" - info = """Get information about domain consoles.""" - - def main(self, args): - l = server.xend_consoles() - print "Dom Port Id" - for x in l: - info = server.xend_console(x) - d = {} - d['dom'] = sxp.child(info, 'dst', ['dst', '?', '?'])[1] - d['port'] = sxp.child_value(info, 'port', '?') - d['id'] = sxp.child_value(info, 'id', '?') - print "%(dom)3s %(port)4s %(id)3s" % d - -xm.prog(ProgConsoles) - -class ProgConsole(Prog): - group = 'console' - name = "console" - info = """Open a console to a domain.""" - - def help(self, args): - print "console DOM" - print "\nOpen a console to domain DOM." - - def main(self, args): - if len(args) < 2: self.err("%s: Missing domain" % args[0]) - dom = args[1] - info = server.xend_domain(dom) - console = sxp.child(info, "console") - if not console: - self.err("No console information") - port = sxp.child_value(console, "port") - from xenctl import console_client - console_client.connect("localhost", int(port)) - -xm.prog(ProgConsole) - -def main(args): - xm.main(args) diff --git a/tools/xenmgr/lib/xm/opts.py b/tools/xenmgr/lib/xm/opts.py deleted file mode 100644 index 5b9515215d..0000000000 --- a/tools/xenmgr/lib/xm/opts.py +++ /dev/null @@ -1,339 +0,0 @@ -# Copyright (C) 2004 Mike Wray -"""Object-oriented command-line option support. -""" -from getopt import getopt -import os -import os.path -import sys -import types - -class Opt: - """An individual option. - """ - def __init__(self, opts, name, short=None, long=None, - val=None, fn=None, use=None, default=None): - """Create an option. - - opts parent options object - name name of the field it controls - short short (1-char) command line switch (optional) - long long command-line switch. Defaults to option name. - val string used to print option args in help. - If val is not specified the option has no arg. - fn function to call when the option is specified. - use usage (help) string - default default value if not specified on command-line - """ - self.opts = opts - self.name = name - self.short = short - if long is None: - long = name - self.long = long - self.val = val - self.use = use - self.default = default - self.optkeys = [] - if self.short: - self.optkeys.append('-' + self.short) - if self.long: - self.optkeys.append('--' + self.long) - self.fn = fn - self.specified_opt = None - self.specified_val = None - self.value = None - self.set(default) - - def __repr__(self): - return self.name + '=' + str(self.specified_val) - - __str__ = __repr__ - - def set(self, value): - """Set the option value. - """ - self.opts.setopt(self.name, value) - - def get(self): - """Get the option value. - """ - return self.opts.getopt(self.name) - - def append(self, value): - """Append a value to the option value. - """ - v = self.get() or [] - v.append(value) - self.set(v) - - def short_opt(self): - """Short option spec. - """ - if self.short: - if self.val: - return self.short + ':' - else: - return self.short - else: - return None - - def long_opt(self): - """Long option spec. - """ - if self.long: - if self.val: - return self.long + '=' - else: - return self.long - else: - return None - - def show(self): - sep = '' - for x in self.optkeys: - print sep, x, - sep = ',' - if self.val: - print self.val, - print - if self.use: - print '\t', - print self.use - if self.val: - print '\tDefault', self.default or 'None' - - def specify(self, k, v): - """Specify the option. Called when the option is set - from the command line. - - k option switch used - v optional value given (if any) - """ - if k in self.optkeys: - if self.val is None and v: - self.opts.err("Option '%s' does not take a value" % k) - self.specified_opt = k - self.specified_val = v - if self.fn: - self.fn(self, k, v) - return 1 - else: - return 0 - - def specified(self): - """Test whether the option has been specified: set - from the command line. - """ - return self.specified_opt - -class OptVals: - """Class to hold option values. - """ - pass - -class Opts: - """Container for options. - """ - def __init__(self, use=None): - """Options constructor. - - use usage string - """ - self.use = use - # List of options. - self.options = [] - # Options indexed by name. - self.options_map = {} - # Command-line arguments. - self.argv = [] - # Option values. - self.vals = OptVals() - self.vals.quiet = 0 - # Variables for default scripts. - self.vars = {} - - def __repr__(self): - return '\n'.join(map(str, self.options)) - - __str__ = __repr__ - - def opt(self, name, **args): - """Add an option. - - name option name - **args keyword params for option constructor - """ - x = Opt(self, name, **args) - self.options.append(x) - self.options_map[name] = x - return x - - def setvar(self, var, val): - """Set a default script variable. - """ - self.vars[var] = val - - def getvar(self, var): - """Get a default script variable. - """ - return self.vars.get(var) - - def option(self, name): - """Get an option (object). - """ - return self.options_map.get(name) - - def setopt(self, name, val): - """Set an option value. - An option can also be set using 'opts.vals.name = val'. - """ - setattr(self.vals, name, val) - - def getopt(self, name): - """Get an option value. - An option value can also be got using 'opts.vals.name'. - """ - getattr(self.vals, name) - - def specified(self, name): - """Test if an option has been specified. - """ - opt = self.option(name) - return opt and opt.specified() - - def err(self, msg): - """Print an error to stderr and exit. - """ - print >>sys.stderr, "Error:", msg - sys.exit(1) - - def info(self, msg): - """Print a message to stdout (unless quiet is set). - """ - if self.vals.quiet: return - print msg - - def warn(self, msg): - """Print a warning to stdout. - """ - print >>sys.stderr, "Warning:", msg - - def parse(self, argv): - """Parse arguments argv using the options. - - return remaining arguments - """ - self.argv = argv - (vals, args) = getopt(argv[1:], self.short_opts(), self.long_opts()) - self.args = args - for (k, v) in vals: - for opt in self.options: - if opt.specify(k, v): break - else: - print >>sys.stderr, "Error: Unknown option:", k - self.usage() - return args - - def short_opts(self): - """Get short options specifier for getopt. - """ - l = [] - for x in self.options: - y = x.short_opt() - if not y: continue - l.append(y) - return ''.join(l) - - def long_opts(self): - """Get long options specifier for getopt. - """ - l = [] - for x in self.options: - y = x.long_opt() - if not y: continue - l.append(y) - return l - - def usage(self): - print 'Usage: ', self.argv[0], self.use or 'OPTIONS' - for opt in self.options: - opt.show() - - def load_defaults(self): - """Load a defaults script. Assumes these options set: - 'path' search path - 'default' script name - """ - for x in [ '' ] + self.vals.path.split(':'): - if x: - p = os.path.join(x, self.vals.defaults) - else: - p = self.vals.defaults - if os.path.exists(p): - self.load(p) - break - else: - self.err("Cannot open defaults file %s" % self.vals.defaults) - - def load(self, defaults, help=0): - """Load a defaults file. Local variables in the file - are used to set options with the same names. - Variables are not used to set options that are already specified. - """ - # Create global and lobal dicts for the file. - # Initialize locals to the vars. - # Use exec to do the standard imports and - # define variables we are passing to the script. - globals = {} - locals = {} - locals.update(self.vars) - cmd = '\n'.join(["import sys", - "import os", - "import os.path", - "xm_file = '%s'" % defaults, - "xm_help = %d" % help ]) - exec cmd in globals, locals - execfile(defaults, globals, locals) - if help: return - # Extract the values set by the script and set the corresponding - # options, if not set on the command line. - vtypes = [ types.StringType, - types.ListType, - types.IntType, - types.FloatType - ] - for (k, v) in locals.items(): - if self.specified(k): continue - if not(type(v) in vtypes): continue - self.setopt(k, v) - -def set_true(opt, k, v): - """Set an option true.""" - opt.set(1) - -def set_false(opt, k, v): - """Set an option false.""" - opt.set(0) - -def set_value(opt, k, v): - """Set an option to a valoue.""" - opt.set(v) - -def set_int(opt, k, v): - """Set an option to an integer value.""" - try: - v = int(v) - except: - opt.opts.err('Invalid value: ' + str(v)) - opt.set(v) - -def append_value(opt, k, v): - """Append a value to a list option.""" - opt.append(v) - -def set_var(opt, k, v): - """Set a default script variable. - """ - (var, val) = v.strip().split('=') - opt.opts.setvar(var.strip(), val.strip()) - diff --git a/tools/xenmgr/lib/xm/shutdown.py b/tools/xenmgr/lib/xm/shutdown.py deleted file mode 100644 index 2c7831876b..0000000000 --- a/tools/xenmgr/lib/xm/shutdown.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (C) 2004 Mike Wray -"""Domain shutdown. -""" -import string -import sys -import time - -from xenmgr.XendClient import server -from xenmgr.xm.opts import * - -gopts = Opts(use="""[options] [DOM] - -Shutdown one or more domains gracefully.""") - -gopts.opt('help', short='h', - fn=set_true, default=0, - use="Print this help.") - -gopts.opt('all', short='a', - fn=set_true, default=0, - use="Shutdown all domains.") - -gopts.opt('wait', short='w', - fn=set_true, default=0, - use='Wait for shutdown to complete.') - -gopts.opt('norestart', short='n', - fn=set_true, default=0, - use='Prevent domain restart.') - -def shutdown(opts, doms, wait): - def domains(): - return [ int(a) for a in server.xend_domains() ] - if doms == None: doms = domains() - if 0 in doms: - doms.remove(0) - for d in doms: - server.xend_domain_shutdown(d) - if wait: - while doms: - alive = domains() - dead = [] - for d in doms: - if d in alive: continue - dead.append(d) - for d in dead: - opts.info("Domain %d terminated" % d) - doms.remove(d) - time.sleep(1) - opts.info("All domains terminated") - -def main_all(opts, args): - shutdown(opts, None, opts.vals.wait) - -def main_dom(opts, args): - if len(args) < 1: opts.err('Missing domain') - dom = args[0] - try: - domid = int(dom) - except: - opts.err('Invalid domain: ' + dom) - shutdown(opts, [ domid ], opts.vals.wait) - -def main(argv): - opts = gopts - args = opts.parse(argv) - if opts.vals.help: - opts.usage() - return - print 'shutdown.main>', len(args), args - if opts.vals.all: - main_all(opts, args) - else: - main_dom(opts, args) - diff --git a/tools/xenmgr/netfix b/tools/xenmgr/netfix deleted file mode 100644 index 998a78a0d9..0000000000 --- a/tools/xenmgr/netfix +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/python -# -*- mode: python; -*- -# Copyright (C) 2004 Mike Wray -#============================================================================ -# Move the IP address from eth0 onto the Xen bridge (nbe-br). -# Only works if the bridge control utils (brctl) have been installed. -#============================================================================ - -from getopt import getopt -from xenmgr.XendBridge import * - -short_options = 'hvqni:b:c' -long_options = ['help', 'verbose', 'quiet', - 'interface=', 'bridge=', 'create'] - -def usage(): - print """Usage: - %s [options] - - Reconfigure routing so that has the IP address from - . This lets IP carry on working when - is attached to for virtual networking. - Uses brctl to add to , - so this can be run before any domains have been created. - """ % sys.argv[0] - print """ - -i, --interface interface, default %(interface)s. - -b, --bridge bridge, default %(bridge)s. - -c, --create create the bridge. - -v, --verbose Print commands. - -q, --quiet Don't print commands. - -n, --dry-run Don't execute commands. - -h, --help Print this help. - """ % defaults - sys.exit(1) - - -def main(): - lopts = set_opts(Opts(defaults)) - lopts.dryrun = 0 - (options, args) = getopt(sys.argv[1:], short_options, long_options) - if args: usage() - for k, v in options: - if k in ['-h', '--help']: - usage() - elif k in ['-c', '--create']: - lopts.create = 1 - elif k in ['-i', '--interface']: - lopts.interface = v - elif k in ['-b', '--bridge']: - lopts.bridge = v - elif k in ['-q', '--quiet']: - lopts.verbose = 0 - elif k in ['-v', '--verbose']: - lopts.verbose = 1 - elif k in ['-n', '--dry-run']: - lopts.dryrun = 1 - reconfigure(lopts.interface, lopts.bridge) - -if __name__ == '__main__': - main() diff --git a/tools/xenmgr/setup.py b/tools/xenmgr/setup.py deleted file mode 100644 index b7f76139be..0000000000 --- a/tools/xenmgr/setup.py +++ /dev/null @@ -1,14 +0,0 @@ - -from distutils.core import setup, Extension - -PACKAGE = 'xenmgr' -VERSION = '1.0' - -setup(name = PACKAGE, - version = VERSION, - description = 'Xen Management API', - author = 'Mike Wray', - author_email = 'mike.wray@hp.com', - packages = [ PACKAGE, PACKAGE + '.server', PACKAGE + '.xm' ], - package_dir = { PACKAGE: 'lib' }, - ) diff --git a/tools/xenmgr/xend b/tools/xenmgr/xend deleted file mode 100644 index e575eeccd5..0000000000 --- a/tools/xenmgr/xend +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/python -# -*- mode: python; -*- -# Copyright (C) 2004 Mike Wray - -"""Xen management daemon. Lives in /usr/sbin. - Provides console server and HTTP management api. - - Run: - - xend start - - The daemon is stopped with: - - xend stop - - Unfortunately restarting it upsets the channel to dom0 and - domain management stops working - needs a reboot to fix. -""" -import os -import sys -from xenmgr.server import SrvConsoleServer - -def main(): - daemon = SrvConsoleServer.instance() - if not sys.argv[1:]: - print 'usage: %s {start|stop|restart}' % sys.argv[0] - elif os.fork(): - pid, status = os.wait() - return status >> 8 - elif sys.argv[1] == 'start': - return daemon.start() - elif sys.argv[1] == 'stop': - return daemon.stop() - elif sys.argv[1] == 'restart': - return daemon.stop() or daemon.start() - else: - print 'not an option:', sys.argv[1] - return 1 - -if __name__ == '__main__': - sys.exit(main()) diff --git a/tools/xenmgr/xenmgrd b/tools/xenmgr/xenmgrd deleted file mode 100755 index c871d4dece..0000000000 --- a/tools/xenmgr/xenmgrd +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/python -# Copyright (C) 2004 Mike Wray - -"""Xen management daemon. Lives in /usr/sbin. - Run after running xend to provide http access to the management API. - - NO LONGER NEEDED. -""" -from xenmgr.server import SrvServer -SrvServer.main() diff --git a/tools/xenmgr/xm b/tools/xenmgr/xm deleted file mode 100755 index ff5bbd75ca..0000000000 --- a/tools/xenmgr/xm +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/python -# -*- mode: python; -*- -import sys -from xenmgr.xm import main - -main.main(sys.argv) diff --git a/tools/xentrace/Makefile b/tools/xentrace/Makefile index 6a03b42499..cce1150b16 100644 --- a/tools/xentrace/Makefile +++ b/tools/xentrace/Makefile @@ -3,7 +3,7 @@ CC = gcc CFLAGS = -Wall -O3 -Werror CFLAGS += -I../../xen/include/hypervisor-ifs CFLAGS += -I../../linux-xen-sparse/include -CFLAGS += -I../xend/lib +CFLAGS += -I../xu/lib HDRS = $(wildcard *.h) OBJS = $(patsubst %.c,%.o,$(wildcard *.c)) diff --git a/tools/xu/lib/__init__.py b/tools/xu/lib/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/xu/lib/domain_controller.h b/tools/xu/lib/domain_controller.h new file mode 100644 index 0000000000..76dd164fcb --- /dev/null +++ b/tools/xu/lib/domain_controller.h @@ -0,0 +1,532 @@ +/****************************************************************************** + * domain_controller.h + * + * Interface to server controller (e.g., 'xend'). This header file defines the + * interface that is shared with guest OSes. + * + * Copyright (c) 2004, K A Fraser + */ + +#ifndef __DOMAIN_CONTROLLER_H__ +#define __DOMAIN_CONTROLLER_H__ + + +#ifndef BASIC_START_INFO +#error "Xen header file hypervisor-if.h must already be included here." +#endif + + +/* + * EXTENDED BOOTSTRAP STRUCTURE FOR NEW DOMAINS. + */ + +typedef struct { + BASIC_START_INFO; + u16 domain_controller_evtchn; /* 320 */ +} PACKED extended_start_info_t; /* 322 bytes */ +#define SIF_BLK_BE_DOMAIN (1<<4) /* Is this a block backend domain? */ +#define SIF_NET_BE_DOMAIN (1<<5) /* Is this a net backend domain? */ + + +/* + * Reason codes for SCHEDOP_shutdown. These are opaque to Xen but may be + * interpreted by control software to determine the appropriate action. These + * are only really advisories: the controller can actually do as it likes. + */ +#define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */ +#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */ +#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ + + +/* + * CONTROLLER MESSAGING INTERFACE. + */ + +typedef struct { + u8 type; /* 0: echoed in response */ + u8 subtype; /* 1: echoed in response */ + u8 id; /* 2: echoed in response */ + u8 length; /* 3: number of bytes in 'msg' */ + u8 msg[60]; /* 4: type-specific message data */ +} PACKED control_msg_t; /* 64 bytes */ + +#define CONTROL_RING_SIZE 8 +typedef u32 CONTROL_RING_IDX; +#define MASK_CONTROL_IDX(_i) ((_i)&(CONTROL_RING_SIZE-1)) + +typedef struct { + control_msg_t tx_ring[CONTROL_RING_SIZE]; /* 0: guest -> controller */ + control_msg_t rx_ring[CONTROL_RING_SIZE]; /* 512: controller -> guest */ + CONTROL_RING_IDX tx_req_prod, tx_resp_prod; /* 1024, 1028 */ + CONTROL_RING_IDX rx_req_prod, rx_resp_prod; /* 1032, 1036 */ +} PACKED control_if_t; /* 1040 bytes */ + +/* + * Top-level command types. + */ +#define CMSG_CONSOLE 0 /* Console */ +#define CMSG_BLKIF_BE 1 /* Block-device backend */ +#define CMSG_BLKIF_FE 2 /* Block-device frontend */ +#define CMSG_NETIF_BE 3 /* Network-device backend */ +#define CMSG_NETIF_FE 4 /* Network-device frontend */ +#define CMSG_SHUTDOWN 6 /* Shutdown messages */ + + +/****************************************************************************** + * CONSOLE DEFINITIONS + */ + +/* + * Subtypes for console messages. + */ +#define CMSG_CONSOLE_DATA 0 + + +/****************************************************************************** + * BLOCK-INTERFACE FRONTEND DEFINITIONS + */ + +/* Messages from domain controller to guest. */ +#define CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED 0 + +/* Messages from guest to domain controller. */ +#define CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED 32 +#define CMSG_BLKIF_FE_INTERFACE_CONNECT 33 +#define CMSG_BLKIF_FE_INTERFACE_DISCONNECT 34 + +/* These are used by both front-end and back-end drivers. */ +#define blkif_vdev_t u16 +#define blkif_pdev_t u16 +#define blkif_sector_t u64 + +/* + * CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED: + * Notify a guest about a status change on one of its block interfaces. + * If the interface is DESTROYED or DOWN then the interface is disconnected: + * 1. The shared-memory frame is available for reuse. + * 2. Any unacknowledged messgaes pending on the interface were dropped. + */ +#define BLKIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */ +#define BLKIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */ +#define BLKIF_INTERFACE_STATUS_CONNECTED 2 /* Exists and is connected. */ +typedef struct { + u32 handle; /* 0 */ + u32 status; /* 4 */ + u16 evtchn; /* 8: (only if status == BLKIF_INTERFACE_STATUS_CONNECTED). */ +} PACKED blkif_fe_interface_status_changed_t; /* 10 bytes */ + +/* + * CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED: + * Notify the domain controller that the front-end driver is DOWN or UP. + * When the driver goes DOWN then the controller will send no more + * status-change notifications. When the driver comes UP then the controller + * will send a notification for each interface that currently exists. + * If the driver goes DOWN while interfaces are still UP, the domain + * will automatically take the interfaces DOWN. + */ +#define BLKIF_DRIVER_STATUS_DOWN 0 +#define BLKIF_DRIVER_STATUS_UP 1 +typedef struct { + /* IN */ + u32 status; /* 0: BLKIF_DRIVER_STATUS_??? */ + /* OUT */ + /* + * Tells driver how many interfaces it should expect to immediately + * receive notifications about. + */ + u32 nr_interfaces; /* 4 */ +} PACKED blkif_fe_driver_status_changed_t; /* 8 bytes */ + +/* + * CMSG_BLKIF_FE_INTERFACE_CONNECT: + * If successful, the domain controller will acknowledge with a + * STATUS_CONNECTED message. + */ +typedef struct { + u32 handle; /* 0 */ + u32 __pad; + memory_t shmem_frame; /* 8 */ + MEMORY_PADDING; +} PACKED blkif_fe_interface_connect_t; /* 16 bytes */ + +/* + * CMSG_BLKIF_FE_INTERFACE_DISCONNECT: + * If successful, the domain controller will acknowledge with a + * STATUS_DISCONNECTED message. + */ +typedef struct { + u32 handle; /* 0 */ +} PACKED blkif_fe_interface_disconnect_t; /* 4 bytes */ + + +/****************************************************************************** + * BLOCK-INTERFACE BACKEND DEFINITIONS + */ + +/* Messages from domain controller. */ +#define CMSG_BLKIF_BE_CREATE 0 /* Create a new block-device interface. */ +#define CMSG_BLKIF_BE_DESTROY 1 /* Destroy a block-device interface. */ +#define CMSG_BLKIF_BE_CONNECT 2 /* Connect i/f to remote driver. */ +#define CMSG_BLKIF_BE_DISCONNECT 3 /* Disconnect i/f from remote driver. */ +#define CMSG_BLKIF_BE_VBD_CREATE 4 /* Create a new VBD for an interface. */ +#define CMSG_BLKIF_BE_VBD_DESTROY 5 /* Delete a VBD from an interface. */ +#define CMSG_BLKIF_BE_VBD_GROW 6 /* Append an extent to a given VBD. */ +#define CMSG_BLKIF_BE_VBD_SHRINK 7 /* Remove last extent from a given VBD. */ + +/* Messages to domain controller. */ +#define CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED 32 + +/* + * Message request/response definitions for block-device messages. + */ + +typedef struct { + blkif_sector_t sector_start; /* 0 */ + blkif_sector_t sector_length; /* 8 */ + blkif_pdev_t device; /* 16 */ + u16 __pad; /* 18 */ +} PACKED blkif_extent_t; /* 20 bytes */ + +/* Non-specific 'okay' return. */ +#define BLKIF_BE_STATUS_OKAY 0 +/* Non-specific 'error' return. */ +#define BLKIF_BE_STATUS_ERROR 1 +/* The following are specific error returns. */ +#define BLKIF_BE_STATUS_INTERFACE_EXISTS 2 +#define BLKIF_BE_STATUS_INTERFACE_NOT_FOUND 3 +#define BLKIF_BE_STATUS_INTERFACE_CONNECTED 4 +#define BLKIF_BE_STATUS_VBD_EXISTS 5 +#define BLKIF_BE_STATUS_VBD_NOT_FOUND 6 +#define BLKIF_BE_STATUS_OUT_OF_MEMORY 7 +#define BLKIF_BE_STATUS_EXTENT_NOT_FOUND 8 +#define BLKIF_BE_STATUS_MAPPING_ERROR 9 + +/* This macro can be used to create an array of descriptive error strings. */ +#define BLKIF_BE_STATUS_ERRORS { \ + "Okay", \ + "Non-specific error", \ + "Interface already exists", \ + "Interface not found", \ + "Interface is still connected", \ + "VBD already exists", \ + "VBD not found", \ + "Out of memory", \ + "Extent not found for VBD", \ + "Could not map domain memory" } + +/* + * CMSG_BLKIF_BE_CREATE: + * When the driver sends a successful response then the interface is fully + * created. The controller will send a DOWN notification to the front-end + * driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 blkif_handle; /* 4: Domain-specific interface handle. */ + /* OUT */ + u32 status; /* 8 */ +} PACKED blkif_be_create_t; /* 12 bytes */ + +/* + * CMSG_BLKIF_BE_DESTROY: + * When the driver sends a successful response then the interface is fully + * torn down. The controller will send a DESTROYED notification to the + * front-end driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify interface to be destroyed. */ + u32 blkif_handle; /* 4: ...ditto... */ + /* OUT */ + u32 status; /* 8 */ +} PACKED blkif_be_destroy_t; /* 12 bytes */ + +/* + * CMSG_BLKIF_BE_CONNECT: + * When the driver sends a successful response then the interface is fully + * connected. The controller will send a CONNECTED notification to the + * front-end driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 blkif_handle; /* 4: Domain-specific interface handle. */ + memory_t shmem_frame; /* 8: Page cont. shared comms window. */ + MEMORY_PADDING; + u32 evtchn; /* 16: Event channel for notifications. */ + /* OUT */ + u32 status; /* 20 */ +} PACKED blkif_be_connect_t; /* 24 bytes */ + +/* + * CMSG_BLKIF_BE_DISCONNECT: + * When the driver sends a successful response then the interface is fully + * disconnected. The controller will send a DOWN notification to the front-end + * driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 blkif_handle; /* 4: Domain-specific interface handle. */ + /* OUT */ + u32 status; /* 8 */ +} PACKED blkif_be_disconnect_t; /* 12 bytes */ + +/* CMSG_BLKIF_BE_VBD_CREATE */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify blkdev interface. */ + u32 blkif_handle; /* 4: ...ditto... */ + blkif_vdev_t vdevice; /* 8: Interface-specific id for this VBD. */ + u16 readonly; /* 10: Non-zero -> VBD isn't writeable. */ + /* OUT */ + u32 status; /* 12 */ +} PACKED blkif_be_vbd_create_t; /* 16 bytes */ + +/* CMSG_BLKIF_BE_VBD_DESTROY */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify blkdev interface. */ + u32 blkif_handle; /* 4: ...ditto... */ + blkif_vdev_t vdevice; /* 8: Interface-specific id of the VBD. */ + u16 __pad; /* 10 */ + /* OUT */ + u32 status; /* 12 */ +} PACKED blkif_be_vbd_destroy_t; /* 16 bytes */ + +/* CMSG_BLKIF_BE_VBD_GROW */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify blkdev interface. */ + u32 blkif_handle; /* 4: ...ditto... */ + blkif_extent_t extent; /* 8: Physical extent to append to VBD. */ + blkif_vdev_t vdevice; /* 28: Interface-specific id of the VBD. */ + u16 __pad; /* 30 */ + /* OUT */ + u32 status; /* 32 */ +} PACKED blkif_be_vbd_grow_t; /* 36 bytes */ + +/* CMSG_BLKIF_BE_VBD_SHRINK */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify blkdev interface. */ + u32 blkif_handle; /* 4: ...ditto... */ + blkif_vdev_t vdevice; /* 8: Interface-specific id of the VBD. */ + u16 __pad; /* 10 */ + /* OUT */ + u32 status; /* 12 */ +} PACKED blkif_be_vbd_shrink_t; /* 16 bytes */ + +/* + * CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: + * Notify the domain controller that the back-end driver is DOWN or UP. + * If the driver goes DOWN while interfaces are still UP, the controller + * will automatically send DOWN notifications. + */ +typedef struct { + u32 status; /* 0: BLKIF_DRIVER_STATUS_??? */ +} PACKED blkif_be_driver_status_changed_t; /* 4 bytes */ + + +/****************************************************************************** + * NETWORK-INTERFACE FRONTEND DEFINITIONS + */ + +/* Messages from domain controller to guest. */ +#define CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED 0 + +/* Messages from guest to domain controller. */ +#define CMSG_NETIF_FE_DRIVER_STATUS_CHANGED 32 +#define CMSG_NETIF_FE_INTERFACE_CONNECT 33 +#define CMSG_NETIF_FE_INTERFACE_DISCONNECT 34 + +/* + * CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED: + * Notify a guest about a status change on one of its network interfaces. + * If the interface is DESTROYED or DOWN then the interface is disconnected: + * 1. The shared-memory frame is available for reuse. + * 2. Any unacknowledged messgaes pending on the interface were dropped. + */ +#define NETIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */ +#define NETIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */ +#define NETIF_INTERFACE_STATUS_CONNECTED 2 /* Exists and is connected. */ +typedef struct { + u32 handle; /* 0 */ + u32 status; /* 4 */ + u16 evtchn; /* 8: status == NETIF_INTERFACE_STATUS_CONNECTED */ + u8 mac[6]; /* 10: status == NETIF_INTERFACE_STATUS_CONNECTED */ +} PACKED netif_fe_interface_status_changed_t; /* 16 bytes */ + +/* + * CMSG_NETIF_FE_DRIVER_STATUS_CHANGED: + * Notify the domain controller that the front-end driver is DOWN or UP. + * When the driver goes DOWN then the controller will send no more + * status-change notifications. When the driver comes UP then the controller + * will send a notification for each interface that currently exists. + * If the driver goes DOWN while interfaces are still UP, the domain + * will automatically take the interfaces DOWN. + */ +#define NETIF_DRIVER_STATUS_DOWN 0 +#define NETIF_DRIVER_STATUS_UP 1 +typedef struct { + /* IN */ + u32 status; /* 0: NETIF_DRIVER_STATUS_??? */ + /* OUT */ + /* + * Tells driver how many interfaces it should expect to immediately + * receive notifications about. + */ + u32 nr_interfaces; /* 4 */ +} PACKED netif_fe_driver_status_changed_t; /* 8 bytes */ + +/* + * CMSG_NETIF_FE_INTERFACE_CONNECT: + * If successful, the domain controller will acknowledge with a + * STATUS_CONNECTED message. + */ +typedef struct { + u32 handle; /* 0 */ + u32 __pad; /* 4 */ + memory_t tx_shmem_frame; /* 8 */ + MEMORY_PADDING; + memory_t rx_shmem_frame; /* 16 */ + MEMORY_PADDING; +} PACKED netif_fe_interface_connect_t; /* 24 bytes */ + +/* + * CMSG_NETIF_FE_INTERFACE_DISCONNECT: + * If successful, the domain controller will acknowledge with a + * STATUS_DISCONNECTED message. + */ +typedef struct { + u32 handle; /* 0 */ +} PACKED netif_fe_interface_disconnect_t; /* 4 bytes */ + + +/****************************************************************************** + * NETWORK-INTERFACE BACKEND DEFINITIONS + */ + +/* Messages from domain controller. */ +#define CMSG_NETIF_BE_CREATE 0 /* Create a new net-device interface. */ +#define CMSG_NETIF_BE_DESTROY 1 /* Destroy a net-device interface. */ +#define CMSG_NETIF_BE_CONNECT 2 /* Connect i/f to remote driver. */ +#define CMSG_NETIF_BE_DISCONNECT 3 /* Disconnect i/f from remote driver. */ + +/* Messages to domain controller. */ +#define CMSG_NETIF_BE_DRIVER_STATUS_CHANGED 32 + +/* + * Message request/response definitions for net-device messages. + */ + +/* Non-specific 'okay' return. */ +#define NETIF_BE_STATUS_OKAY 0 +/* Non-specific 'error' return. */ +#define NETIF_BE_STATUS_ERROR 1 +/* The following are specific error returns. */ +#define NETIF_BE_STATUS_INTERFACE_EXISTS 2 +#define NETIF_BE_STATUS_INTERFACE_NOT_FOUND 3 +#define NETIF_BE_STATUS_INTERFACE_CONNECTED 4 +#define NETIF_BE_STATUS_OUT_OF_MEMORY 5 +#define NETIF_BE_STATUS_MAPPING_ERROR 6 + +/* This macro can be used to create an array of descriptive error strings. */ +#define NETIF_BE_STATUS_ERRORS { \ + "Okay", \ + "Non-specific error", \ + "Interface already exists", \ + "Interface not found", \ + "Interface is still connected", \ + "Out of memory", \ + "Could not map domain memory" } + +/* + * CMSG_NETIF_BE_CREATE: + * When the driver sends a successful response then the interface is fully + * created. The controller will send a DOWN notification to the front-end + * driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 netif_handle; /* 4: Domain-specific interface handle. */ + u8 mac[6]; /* 8 */ + u16 __pad; /* 14 */ + /* OUT */ + u32 status; /* 16 */ +} PACKED netif_be_create_t; /* 20 bytes */ + +/* + * CMSG_NETIF_BE_DESTROY: + * When the driver sends a successful response then the interface is fully + * torn down. The controller will send a DESTROYED notification to the + * front-end driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Identify interface to be destroyed. */ + u32 netif_handle; /* 4: ...ditto... */ + /* OUT */ + u32 status; /* 8 */ +} PACKED netif_be_destroy_t; /* 12 bytes */ + +/* + * CMSG_NETIF_BE_CONNECT: + * When the driver sends a successful response then the interface is fully + * connected. The controller will send a CONNECTED notification to the + * front-end driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 netif_handle; /* 4: Domain-specific interface handle. */ + memory_t tx_shmem_frame; /* 8: Page cont. tx shared comms window. */ + MEMORY_PADDING; + memory_t rx_shmem_frame; /* 16: Page cont. rx shared comms window. */ + MEMORY_PADDING; + u16 evtchn; /* 24: Event channel for notifications. */ + u16 __pad; /* 26 */ + /* OUT */ + u32 status; /* 28 */ +} PACKED netif_be_connect_t; /* 32 bytes */ + +/* + * CMSG_NETIF_BE_DISCONNECT: + * When the driver sends a successful response then the interface is fully + * disconnected. The controller will send a DOWN notification to the front-end + * driver. + */ +typedef struct { + /* IN */ + domid_t domid; /* 0: Domain attached to new interface. */ + u32 netif_handle; /* 4: Domain-specific interface handle. */ + /* OUT */ + u32 status; /* 8 */ +} PACKED netif_be_disconnect_t; /* 12 bytes */ + +/* + * CMSG_NETIF_BE_DRIVER_STATUS_CHANGED: + * Notify the domain controller that the back-end driver is DOWN or UP. + * If the driver goes DOWN while interfaces are still UP, the domain + * will automatically send DOWN notifications. + */ +typedef struct { + u32 status; /* 0: NETIF_DRIVER_STATUS_??? */ +} PACKED netif_be_driver_status_changed_t; /* 4 bytes */ + + +/****************************************************************************** + * SHUTDOWN DEFINITIONS + */ + +/* + * Subtypes for shutdown messages. + */ +#define CMSG_SHUTDOWN_POWEROFF 0 /* Clean shutdown (SHUTDOWN_poweroff). */ +#define CMSG_SHUTDOWN_REBOOT 1 /* Clean shutdown (SHUTDOWN_reboot). */ +#define CMSG_SHUTDOWN_SUSPEND 2 /* Create suspend info, then */ + /* SHUTDOWN_suspend. */ + +#endif /* __DOMAIN_CONTROLLER_H__ */ diff --git a/tools/xu/lib/xu.c b/tools/xu/lib/xu.c new file mode 100644 index 0000000000..48c975912d --- /dev/null +++ b/tools/xu/lib/xu.c @@ -0,0 +1,1386 @@ +/****************************************************************************** + * utils.c + * + * Copyright (c) 2004, K A Fraser + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "domain_controller.h" + +#include + +#define XENPKG "xen.ext.xu" + +/* Needed for Python versions earlier than 2.3. */ +#ifndef PyMODINIT_FUNC +#define PyMODINIT_FUNC DL_EXPORT(void) +#endif + +/* NB. The following should be kept in sync with the kernel's evtchn driver. */ +#define EVTCHN_DEV_NAME "/dev/xen/evtchn" +#define EVTCHN_DEV_MAJOR 10 +#define EVTCHN_DEV_MINOR 200 +#define PORT_NORMAL 0x0000 /* A standard event notification. */ +#define PORT_EXCEPTION 0x8000 /* An exceptional notification. */ +#define PORTIDX_MASK 0x7fff /* Strip subtype to obtain port index. */ +/* /dev/xen/evtchn ioctls: */ +/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */ +#define EVTCHN_RESET _IO('E', 1) +/* EVTCHN_BIND: Bind to teh specified event-channel port. */ +#define EVTCHN_BIND _IO('E', 2) +/* EVTCHN_UNBIND: Unbind from the specified event-channel port. */ +#define EVTCHN_UNBIND _IO('E', 3) + +/* Size of a machine page frame. */ +#define PAGE_SIZE 4096 + + +/* + * *********************** NOTIFIER *********************** + */ + +typedef struct { + PyObject_HEAD; + int evtchn_fd; +} xu_notifier_object; + +static PyObject *xu_notifier_read(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + u16 v; + int bytes; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + while ( (bytes = read(xun->evtchn_fd, &v, sizeof(v))) == -1 ) + { + if ( errno == EINTR ) + continue; + if ( errno == EAGAIN ) + goto none; + return PyErr_SetFromErrno(PyExc_IOError); + } + + if ( bytes == sizeof(v) ) + return Py_BuildValue("(i,i)", v&PORTIDX_MASK, v&~PORTIDX_MASK); + + none: + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_unmask(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + u16 v; + int idx; + + if ( !PyArg_ParseTuple(args, "i", &idx) ) + return NULL; + + v = (u16)idx; + + (void)write(xun->evtchn_fd, &v, sizeof(v)); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_bind(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + int idx; + + if ( !PyArg_ParseTuple(args, "i", &idx) ) + return NULL; + + if ( ioctl(xun->evtchn_fd, EVTCHN_BIND, idx) != 0 ) + return PyErr_SetFromErrno(PyExc_IOError); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_unbind(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + int idx; + + if ( !PyArg_ParseTuple(args, "i", &idx) ) + return NULL; + + if ( ioctl(xun->evtchn_fd, EVTCHN_UNBIND, idx) != 0 ) + return PyErr_SetFromErrno(PyExc_IOError); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + return PyInt_FromLong(xun->evtchn_fd); +} + +static PyMethodDef xu_notifier_methods[] = { + { "read", + (PyCFunction)xu_notifier_read, + METH_VARARGS, + "Read a (@port, @type) pair.\n" }, + + { "unmask", + (PyCFunction)xu_notifier_unmask, + METH_VARARGS, + "Unmask notifications for a @port.\n" }, + + { "bind", + (PyCFunction)xu_notifier_bind, + METH_VARARGS, + "Get notifications for a @port.\n" }, + + { "unbind", + (PyCFunction)xu_notifier_unbind, + METH_VARARGS, + "No longer get notifications for a @port.\n" }, + + { "fileno", + (PyCFunction)xu_notifier_fileno, + METH_VARARGS, + "Return the file descriptor for the notification channel.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_notifier_type; + +static PyObject *xu_notifier_new(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + xun = PyObject_New(xu_notifier_object, &xu_notifier_type); + + reopen: + xun->evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR); + if ( xun->evtchn_fd == -1 ) + { + if ( (errno == ENOENT) && + ((mkdir("/dev/xen", 0755) == 0) || (errno == EEXIST)) && + (mknod(EVTCHN_DEV_NAME, S_IFCHR|0600, + (EVTCHN_DEV_MAJOR << 8) | EVTCHN_DEV_MINOR) == 0) ) + goto reopen; + PyObject_Del((PyObject *)xun); + return PyErr_SetFromErrno(PyExc_IOError); + } + + return (PyObject *)xun; +} + +static PyObject *xu_notifier_getattr(PyObject *obj, char *name) +{ + if ( strcmp(name, "EXCEPTION") == 0 ) + return PyInt_FromLong(PORT_EXCEPTION); + if ( strcmp(name, "NORMAL") == 0 ) + return PyInt_FromLong(PORT_NORMAL); + return Py_FindMethod(xu_notifier_methods, obj, name); +} + +static void xu_notifier_dealloc(PyObject *self) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + (void)close(xun->evtchn_fd); + PyObject_Del(self); +} + +static PyTypeObject xu_notifier_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "notifier", + sizeof(xu_notifier_object), + 0, + xu_notifier_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_notifier_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** MESSAGE *********************** + */ + +#define TYPE(_x,_y) (((_x)<<8)|(_y)) +#define P2C(_struct, _field, _ctype) \ + do { \ + PyObject *obj; \ + if ( (obj = PyDict_GetItemString(payload, #_field)) != NULL ) \ + { \ + if ( PyInt_Check(obj) ) \ + { \ + ((_struct *)&xum->msg.msg[0])->_field = \ + (_ctype)PyInt_AsLong(obj); \ + dict_items_parsed++; \ + } \ + else if ( PyLong_Check(obj) ) \ + { \ + ((_struct *)&xum->msg.msg[0])->_field = \ + (_ctype)PyLong_AsUnsignedLongLong(obj); \ + dict_items_parsed++; \ + } \ + } \ + xum->msg.length = sizeof(_struct); \ + } while ( 0 ) +#define C2P(_struct, _field, _pytype, _ctype) \ + do { \ + PyObject *obj = Py ## _pytype ## _From ## _ctype \ + (((_struct *)&xum->msg.msg[0])->_field); \ + if ( dict == NULL ) dict = PyDict_New(); \ + PyDict_SetItemString(dict, #_field, obj); \ + } while ( 0 ) + +typedef struct { + PyObject_HEAD; + control_msg_t msg; +} xu_message_object; + +static PyObject *xu_message_append_payload(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + char *str; + int len; + + if ( !PyArg_ParseTuple(args, "s#", &str, &len) ) + return NULL; + + if ( (len + xum->msg.length) > sizeof(xum->msg.msg) ) + { + PyErr_SetString(PyExc_RuntimeError, "out of space in control message"); + return NULL; + } + + memcpy(&xum->msg.msg[xum->msg.length], str, len); + xum->msg.length += len; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_message_set_response_fields(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + PyObject *payload; + int dict_items_parsed = 0; + + if ( !PyArg_ParseTuple(args, "O", &payload) ) + return NULL; + + if ( !PyDict_Check(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload is not a dictionary"); + return NULL; + } + + switch ( TYPE(xum->msg.type, xum->msg.subtype) ) + { + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED): + P2C(blkif_fe_driver_status_changed_t, nr_interfaces, u32); + break; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): + P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32); + break; + } + + if ( dict_items_parsed != PyDict_Size(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload contains bad items"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_message_get_payload(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + PyObject *dict = NULL; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + switch ( TYPE(xum->msg.type, xum->msg.subtype) ) + { + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED): + C2P(blkif_fe_interface_status_changed_t, handle, Int, Long); + C2P(blkif_fe_interface_status_changed_t, status, Int, Long); + C2P(blkif_fe_interface_status_changed_t, evtchn, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED): + C2P(blkif_fe_driver_status_changed_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT): + C2P(blkif_fe_interface_connect_t, handle, Int, Long); + C2P(blkif_fe_interface_connect_t, shmem_frame, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_DISCONNECT): + C2P(blkif_fe_interface_disconnect_t, handle, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE): + C2P(blkif_be_create_t, domid, Int, Long); + C2P(blkif_be_create_t, blkif_handle, Int, Long); + C2P(blkif_be_create_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY): + C2P(blkif_be_destroy_t, domid, Int, Long); + C2P(blkif_be_destroy_t, blkif_handle, Int, Long); + C2P(blkif_be_destroy_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT): + C2P(blkif_be_connect_t, domid, Int, Long); + C2P(blkif_be_connect_t, blkif_handle, Int, Long); + C2P(blkif_be_connect_t, shmem_frame, Int, Long); + C2P(blkif_be_connect_t, evtchn, Int, Long); + C2P(blkif_be_connect_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT): + C2P(blkif_be_disconnect_t, domid, Int, Long); + C2P(blkif_be_disconnect_t, blkif_handle, Int, Long); + C2P(blkif_be_disconnect_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE): + C2P(blkif_be_vbd_create_t, domid, Int, Long); + C2P(blkif_be_vbd_create_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_create_t, vdevice, Int, Long); + C2P(blkif_be_vbd_create_t, readonly, Int, Long); + C2P(blkif_be_vbd_create_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY): + C2P(blkif_be_vbd_destroy_t, domid, Int, Long); + C2P(blkif_be_vbd_destroy_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_destroy_t, vdevice, Int, Long); + C2P(blkif_be_vbd_destroy_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW): + C2P(blkif_be_vbd_grow_t, domid, Int, Long); + C2P(blkif_be_vbd_grow_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_grow_t, vdevice, Int, Long); + C2P(blkif_be_vbd_grow_t, extent.sector_start, + Long, UnsignedLongLong); + C2P(blkif_be_vbd_grow_t, extent.sector_length, + Long, UnsignedLongLong); + C2P(blkif_be_vbd_grow_t, extent.device, Int, Long); + C2P(blkif_be_vbd_grow_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_SHRINK): + C2P(blkif_be_vbd_shrink_t, domid, Int, Long); + C2P(blkif_be_vbd_shrink_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_shrink_t, vdevice, Int, Long); + C2P(blkif_be_vbd_shrink_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED): + C2P(blkif_be_driver_status_changed_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED): + C2P(netif_fe_interface_status_changed_t, handle, Int, Long); + C2P(netif_fe_interface_status_changed_t, status, Int, Long); + C2P(netif_fe_interface_status_changed_t, evtchn, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): + C2P(netif_fe_driver_status_changed_t, status, Int, Long); + C2P(netif_fe_driver_status_changed_t, nr_interfaces, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT): + C2P(netif_fe_interface_connect_t, handle, Int, Long); + C2P(netif_fe_interface_connect_t, tx_shmem_frame, Int, Long); + C2P(netif_fe_interface_connect_t, rx_shmem_frame, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_DISCONNECT): + C2P(netif_fe_interface_disconnect_t, handle, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE): + C2P(netif_be_create_t, domid, Int, Long); + C2P(netif_be_create_t, netif_handle, Int, Long); + C2P(netif_be_create_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY): + C2P(netif_be_destroy_t, domid, Int, Long); + C2P(netif_be_destroy_t, netif_handle, Int, Long); + C2P(netif_be_destroy_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT): + C2P(netif_be_connect_t, domid, Int, Long); + C2P(netif_be_connect_t, netif_handle, Int, Long); + C2P(netif_be_connect_t, tx_shmem_frame, Int, Long); + C2P(netif_be_connect_t, rx_shmem_frame, Int, Long); + C2P(netif_be_connect_t, evtchn, Int, Long); + C2P(netif_be_connect_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT): + C2P(netif_be_disconnect_t, domid, Int, Long); + C2P(netif_be_disconnect_t, netif_handle, Int, Long); + C2P(netif_be_disconnect_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED): + C2P(netif_be_driver_status_changed_t, status, Int, Long); + return dict; + } + + return PyString_FromStringAndSize(xum->msg.msg, xum->msg.length); +} + +static PyObject *xu_message_get_header(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + return Py_BuildValue("{s:i,s:i,s:i}", + "type", xum->msg.type, + "subtype", xum->msg.subtype, + "id", xum->msg.id); +} + +static PyMethodDef xu_message_methods[] = { + { "append_payload", + (PyCFunction)xu_message_append_payload, + METH_VARARGS, + "Append @str to the message payload.\n" }, + + { "set_response_fields", + (PyCFunction)xu_message_set_response_fields, + METH_VARARGS, + "Fill in the response fields in a message that was passed to us.\n" }, + + { "get_payload", + (PyCFunction)xu_message_get_payload, + METH_VARARGS, + "Return the message payload in string form.\n" }, + + { "get_header", + (PyCFunction)xu_message_get_header, + METH_VARARGS, + "Returns a dictionary of values for @type, @subtype, and @id.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_message_type; + +static PyObject *xu_message_new(PyObject *self, PyObject *args) +{ + xu_message_object *xum; + int type, subtype, id, dict_items_parsed = 0; + PyObject *payload = NULL; + + if ( !PyArg_ParseTuple(args, "iii|O", &type, &subtype, &id, &payload) ) + return NULL; + + xum = PyObject_New(xu_message_object, &xu_message_type); + + xum->msg.type = type; + xum->msg.subtype = subtype; + xum->msg.id = id; + xum->msg.length = 0; + + if ( payload == NULL ) + return (PyObject *)xum; + + if ( !PyDict_Check(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload is not a dictionary"); + PyObject_Del((PyObject *)xum); + return NULL; + } + + switch ( TYPE(type, subtype) ) + { + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED): + P2C(blkif_fe_interface_status_changed_t, handle, u32); + P2C(blkif_fe_interface_status_changed_t, status, u32); + P2C(blkif_fe_interface_status_changed_t, evtchn, u16); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE): + P2C(blkif_be_create_t, domid, u32); + P2C(blkif_be_create_t, blkif_handle, u32); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY): + P2C(blkif_be_destroy_t, domid, u32); + P2C(blkif_be_destroy_t, blkif_handle, u32); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT): + P2C(blkif_be_connect_t, domid, u32); + P2C(blkif_be_connect_t, blkif_handle, u32); + P2C(blkif_be_connect_t, shmem_frame, memory_t); + P2C(blkif_be_connect_t, evtchn, u16); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT): + P2C(blkif_be_disconnect_t, domid, u32); + P2C(blkif_be_disconnect_t, blkif_handle, u32); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE): + P2C(blkif_be_vbd_create_t, domid, u32); + P2C(blkif_be_vbd_create_t, blkif_handle, u32); + P2C(blkif_be_vbd_create_t, vdevice, blkif_vdev_t); + P2C(blkif_be_vbd_create_t, readonly, u16); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY): + P2C(blkif_be_vbd_destroy_t, domid, u32); + P2C(blkif_be_vbd_destroy_t, blkif_handle, u32); + P2C(blkif_be_vbd_destroy_t, vdevice, blkif_vdev_t); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW): + P2C(blkif_be_vbd_grow_t, domid, u32); + P2C(blkif_be_vbd_grow_t, blkif_handle, u32); + P2C(blkif_be_vbd_grow_t, vdevice, blkif_vdev_t); + P2C(blkif_be_vbd_grow_t, extent.sector_start, blkif_sector_t); + P2C(blkif_be_vbd_grow_t, extent.sector_length, blkif_sector_t); + P2C(blkif_be_vbd_grow_t, extent.device, blkif_pdev_t); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_SHRINK): + P2C(blkif_be_vbd_shrink_t, domid, u32); + P2C(blkif_be_vbd_shrink_t, blkif_handle, u32); + P2C(blkif_be_vbd_shrink_t, vdevice, blkif_vdev_t); + break; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED): + P2C(netif_fe_interface_status_changed_t, handle, u32); + P2C(netif_fe_interface_status_changed_t, status, u32); + P2C(netif_fe_interface_status_changed_t, evtchn, u16); + P2C(netif_fe_interface_status_changed_t, mac[0], u8); + P2C(netif_fe_interface_status_changed_t, mac[1], u8); + P2C(netif_fe_interface_status_changed_t, mac[2], u8); + P2C(netif_fe_interface_status_changed_t, mac[3], u8); + P2C(netif_fe_interface_status_changed_t, mac[4], u8); + P2C(netif_fe_interface_status_changed_t, mac[5], u8); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE): + P2C(netif_be_create_t, domid, u32); + P2C(netif_be_create_t, netif_handle, u32); + P2C(netif_be_create_t, mac[0], u8); + P2C(netif_be_create_t, mac[1], u8); + P2C(netif_be_create_t, mac[2], u8); + P2C(netif_be_create_t, mac[3], u8); + P2C(netif_be_create_t, mac[4], u8); + P2C(netif_be_create_t, mac[5], u8); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY): + P2C(netif_be_destroy_t, domid, u32); + P2C(netif_be_destroy_t, netif_handle, u32); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT): + P2C(netif_be_connect_t, domid, u32); + P2C(netif_be_connect_t, netif_handle, u32); + P2C(netif_be_connect_t, tx_shmem_frame, memory_t); + P2C(netif_be_connect_t, rx_shmem_frame, memory_t); + P2C(netif_be_connect_t, evtchn, u16); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT): + P2C(netif_be_disconnect_t, domid, u32); + P2C(netif_be_disconnect_t, netif_handle, u32); + break; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): + P2C(netif_fe_driver_status_changed_t, status, u32); + P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32); + break; + } + + if ( dict_items_parsed != PyDict_Size(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload contains bad items"); + PyObject_Del((PyObject *)xum); + return NULL; + } + + return (PyObject *)xum; +} + +static PyObject *xu_message_getattr(PyObject *obj, char *name) +{ + xu_message_object *xum; + if ( strcmp(name, "MAX_PAYLOAD") == 0 ) + return PyInt_FromLong(sizeof(xum->msg.msg)); + return Py_FindMethod(xu_message_methods, obj, name); +} + +static void xu_message_dealloc(PyObject *self) +{ + PyObject_Del(self); +} + +static PyTypeObject xu_message_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "message", + sizeof(xu_message_object), + 0, + xu_message_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_message_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** PORT *********************** + */ + +static control_if_t *map_control_interface(int fd, unsigned long pfn) +{ + char *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, + MAP_SHARED, fd, pfn * PAGE_SIZE); + if ( vaddr == MAP_FAILED ) + return NULL; + return (control_if_t *)(vaddr + 2048); +} +static void unmap_control_interface(int fd, control_if_t *c) +{ + char *vaddr = (char *)c - 2048; + (void)munmap(vaddr, PAGE_SIZE); +} + +typedef struct xu_port_object { + PyObject_HEAD; + int mem_fd; + int xc_handle; + u32 remote_dom; + int local_port, remote_port; + control_if_t *interface; + CONTROL_RING_IDX tx_req_cons, tx_resp_prod; + CONTROL_RING_IDX rx_req_prod, rx_resp_cons; +} xu_port_object; + +static PyObject *port_error; + +static int xup_connect(xu_port_object *xup, domid_t dom, + int local_port, int remote_port){ + // From our prespective rx = producer, tx = consumer. + int err = 0; + printf("%s> dom=%u %d:%d\n", __FUNCTION__, (unsigned int)dom, + local_port, remote_port); + + // Consumer = tx. + //xup->interface->tx_resp_prod = 0; + //xup->interface->tx_req_prod = 0; + xup->tx_resp_prod = xup->interface->tx_resp_prod; + xup->tx_req_cons = xup->interface->tx_resp_prod; + printf("%s> tx: %u %u : %u %u\n", __FUNCTION__, + (unsigned int)xup->interface->tx_resp_prod, + (unsigned int)xup->tx_resp_prod, + (unsigned int)xup->tx_req_cons, + (unsigned int)xup->interface->tx_req_prod); + + // Producer = rx. + //xup->interface->rx_req_prod = 0; + //xup->interface->rx_resp_prod = 0; + xup->rx_req_prod = xup->interface->rx_req_prod; + xup->rx_resp_cons = xup->interface->rx_resp_prod; + printf("%s> rx: %u %u : %u %u\n", __FUNCTION__, + (unsigned int)xup->rx_resp_cons, + (unsigned int)xup->interface->rx_resp_prod, + (unsigned int)xup->interface->rx_req_prod, + (unsigned int)xup->rx_req_prod); + + xup->remote_dom = dom; + xup->local_port = local_port; + xup->remote_port = remote_port; + + printf("%s< err=%d\n", __FUNCTION__, err); + return err; +} + +static PyObject *xu_port_notify(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + (void)xc_evtchn_send(xup->xc_handle, xup->local_port); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_port_read_request(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX c = xup->tx_req_cons; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->tx_req_prod) || + ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) ) + { + PyErr_SetString(port_error, "no request to read"); + return NULL; + } + + cmsg = &cif->tx_ring[MASK_CONTROL_IDX(c)]; + xum = PyObject_New(xu_message_object, &xu_message_type); + memcpy(&xum->msg, cmsg, sizeof(*cmsg)); + if ( xum->msg.length > sizeof(xum->msg.msg) ) + xum->msg.length = sizeof(xum->msg.msg); + xup->tx_req_cons++; + return (PyObject *)xum; +} + +static PyObject *xu_port_write_request(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX p = xup->rx_req_prod; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) ) + return NULL; + + if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) ) + { + PyErr_SetString(PyExc_TypeError, "expected a " XENPKG ".message"); + return NULL; + } + + if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) ) + { + PyErr_SetString(port_error, "no space to write request"); + return NULL; + } + + cmsg = &cif->rx_ring[MASK_CONTROL_IDX(p)]; + memcpy(cmsg, &xum->msg, sizeof(*cmsg)); + + xup->rx_req_prod = cif->rx_req_prod = p + 1; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_port_read_response(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX c = xup->rx_resp_cons; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) ) + { + PyErr_SetString(port_error, "no response to read"); + return NULL; + } + + cmsg = &cif->rx_ring[MASK_CONTROL_IDX(c)]; + xum = PyObject_New(xu_message_object, &xu_message_type); + memcpy(&xum->msg, cmsg, sizeof(*cmsg)); + if ( xum->msg.length > sizeof(xum->msg.msg) ) + xum->msg.length = sizeof(xum->msg.msg); + xup->rx_resp_cons++; + return (PyObject *)xum; +} + +static PyObject *xu_port_write_response(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX p = xup->tx_resp_prod; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) ) + return NULL; + + if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) ) + { + PyErr_SetString(PyExc_TypeError, "expected a " XENPKG ".message"); + return NULL; + } + + if ( p == xup->tx_req_cons ) + { + PyErr_SetString(port_error, "no space to write response"); + return NULL; + } + + cmsg = &cif->tx_ring[MASK_CONTROL_IDX(p)]; + memcpy(cmsg, &xum->msg, sizeof(*cmsg)); + + xup->tx_resp_prod = cif->tx_resp_prod = p + 1; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_port_request_to_read(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX c = xup->tx_req_cons; + control_if_t *cif = xup->interface; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->tx_req_prod) || + ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyObject *xu_port_space_to_write_request(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX p = xup->rx_req_prod; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX c = xup->rx_resp_cons; + control_if_t *cif = xup->interface; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyObject *xu_port_space_to_write_response( + PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX p = xup->tx_resp_prod; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( p == xup->tx_req_cons ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyMethodDef xu_port_methods[] = { + { "notify", + (PyCFunction)xu_port_notify, + METH_VARARGS, + "Send a notification to the remote end.\n" }, + + { "read_request", + (PyCFunction)xu_port_read_request, + METH_VARARGS, + "Read a request message from the control interface.\n" }, + + { "write_request", + (PyCFunction)xu_port_write_request, + METH_VARARGS, + "Write a request message to the control interface.\n" }, + + { "read_response", + (PyCFunction)xu_port_read_response, + METH_VARARGS, + "Read a response message from the control interface.\n" }, + + { "write_response", + (PyCFunction)xu_port_write_response, + METH_VARARGS, + "Write a response message to the control interface.\n" }, + + { "request_to_read", + (PyCFunction)xu_port_request_to_read, + METH_VARARGS, + "Returns TRUE if there is a request message to read.\n" }, + + { "space_to_write_request", + (PyCFunction)xu_port_space_to_write_request, + METH_VARARGS, + "Returns TRUE if there is space to write a request message.\n" }, + + { "response_to_read", + (PyCFunction)xu_port_response_to_read, + METH_VARARGS, + "Returns TRUE if there is a response message to read.\n" }, + + { "space_to_write_response", + (PyCFunction)xu_port_space_to_write_response, + METH_VARARGS, + "Returns TRUE if there is space to write a response message.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_port_type; + +static PyObject *xu_port_new(PyObject *self, PyObject *args) +{ + xu_port_object *xup; + u32 dom; + int port1, port2; + xc_dominfo_t info; + + if ( !PyArg_ParseTuple(args, "i", &dom) ) + return NULL; + + xup = PyObject_New(xu_port_object, &xu_port_type); + + if ( (xup->mem_fd = open("/dev/mem", O_RDWR)) == -1 ) + { + PyErr_SetString(port_error, "Could not open '/dev/mem'"); + goto fail1; + } + + /* Set the General-Purpose Subject whose page frame will be mapped. */ + (void)ioctl(xup->mem_fd, _IO('M', 1), (unsigned long)dom); + + if ( (xup->xc_handle = xc_interface_open()) == -1 ) + { + PyErr_SetString(port_error, "Could not open Xen control interface"); + goto fail2; + } + + if ( dom == 0 ) + { + /* + * The control-interface event channel for DOM0 is already set up. + * We use an ioctl to discover the port at our end of the channel. + */ + port1 = ioctl(xup->xc_handle, IOCTL_PRIVCMD_INITDOMAIN_EVTCHN, NULL); + port2 = -1; /* We don't need the remote end of the DOM0 link. */ + if ( port1 < 0 ) + { + PyErr_SetString(port_error, "Could not open channel to DOM0"); + goto fail3; + } + } + else if ( xc_evtchn_bind_interdomain(xup->xc_handle, + DOMID_SELF, dom, + &port1, &port2) != 0 ) + { + PyErr_SetString(port_error, "Could not open channel to domain"); + goto fail3; + } + + if ( (xc_domain_getinfo(xup->xc_handle, dom, 1, &info) != 1) || + (info.domid != dom) ) + { + PyErr_SetString(port_error, "Failed to obtain domain status"); + goto fail4; + } + + xup->interface = + map_control_interface(xup->mem_fd, info.shared_info_frame); + if ( xup->interface == NULL ) + { + PyErr_SetString(port_error, "Failed to map domain control interface"); + goto fail4; + } + + xup_connect(xup, dom, port1, port2); + return (PyObject *)xup; + + + fail4: + (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, port1); + fail3: + (void)xc_interface_close(xup->xc_handle); + fail2: + (void)close(xup->mem_fd); + fail1: + PyObject_Del((PyObject *)xup); + return NULL; +} + +static PyObject *xu_port_getattr(PyObject *obj, char *name) +{ + xu_port_object *xup = (xu_port_object *)obj; + if ( strcmp(name, "local_port") == 0 ) + return PyInt_FromLong(xup->local_port); + if ( strcmp(name, "remote_port") == 0 ) + return PyInt_FromLong(xup->remote_port); + if ( strcmp(name, "remote_dom") == 0 ) + return PyInt_FromLong(xup->remote_dom); + return Py_FindMethod(xu_port_methods, obj, name); +} + +static void xu_port_dealloc(PyObject *self) +{ + xu_port_object *xup = (xu_port_object *)self; + unmap_control_interface(xup->mem_fd, xup->interface); + if ( xup->remote_dom != 0 ) + (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, xup->local_port); + (void)xc_interface_close(xup->xc_handle); + (void)close(xup->mem_fd); + PyObject_Del(self); +} + +static PyTypeObject xu_port_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "port", + sizeof(xu_port_object), + 0, + xu_port_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_port_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** BUFFER *********************** + */ + +#define BUFSZ 65536 +#define MASK_BUF_IDX(_i) ((_i)&(BUFSZ-1)) +typedef unsigned int BUF_IDX; + +typedef struct { + PyObject_HEAD; + char *buf; + unsigned int prod, cons; +} xu_buffer_object; + +static PyObject *__xu_buffer_peek(xu_buffer_object *xub, int max) +{ + PyObject *str1, *str2; + int len1, len2, c = MASK_BUF_IDX(xub->cons); + + len1 = xub->prod - xub->cons; + if ( len1 > (BUFSZ - c) ) /* clip to ring wrap */ + len1 = BUFSZ - c; + if ( len1 > max ) /* clip to specified maximum */ + len1 = max; + if ( len1 < 0 ) /* sanity */ + len1 = 0; + + if ( (str1 = PyString_FromStringAndSize(&xub->buf[c], len1)) == NULL ) + return NULL; + + if ( (len1 < (xub->prod - xub->cons)) && (len1 < max) ) + { + len2 = max - len1; + if ( len2 > MASK_BUF_IDX(xub->prod) ) + len2 = MASK_BUF_IDX(xub->prod); + if ( len2 > 0 ) + { + str2 = PyString_FromStringAndSize(&xub->buf[0], len2); + if ( str2 == NULL ) + return NULL; + PyString_ConcatAndDel(&str1, str2); + if ( str1 == NULL ) + return NULL; + } + } + + return str1; +} + +static PyObject *xu_buffer_peek(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + int max = 1024; + + if ( !PyArg_ParseTuple(args, "|i", &max) ) + return NULL; + + return __xu_buffer_peek(xub, max); +} + +static PyObject *xu_buffer_read(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + PyObject *str; + int max = 1024; + + if ( !PyArg_ParseTuple(args, "|i", &max) ) + return NULL; + + if ( (str = __xu_buffer_peek(xub, max)) != NULL ) + xub->cons += PyString_Size(str); + + return str; +} + +static PyObject *xu_buffer_discard(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + int max, len; + + if ( !PyArg_ParseTuple(args, "i", &max) ) + return NULL; + + len = xub->prod - xub->cons; + if ( len > max ) + len = max; + if ( len < 0 ) + len = 0; + + xub->cons += len; + + return PyInt_FromLong(len); +} + +static PyObject *xu_buffer_write(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + char *str; + int len, len1, len2; + + if ( !PyArg_ParseTuple(args, "s#", &str, &len) ) + return NULL; + + len1 = len; + if ( len1 > (BUFSZ - MASK_BUF_IDX(xub->prod)) ) + len1 = BUFSZ - MASK_BUF_IDX(xub->prod); + if ( len1 > (BUFSZ - (xub->prod - xub->cons)) ) + len1 = BUFSZ - (xub->prod - xub->cons); + + if ( len1 == 0 ) + return PyInt_FromLong(0); + + memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[0], len1); + xub->prod += len1; + + if ( len1 < len ) + { + len2 = len - len1; + if ( len2 > (BUFSZ - MASK_BUF_IDX(xub->prod)) ) + len2 = BUFSZ - MASK_BUF_IDX(xub->prod); + if ( len2 > (BUFSZ - (xub->prod - xub->cons)) ) + len2 = BUFSZ - (xub->prod - xub->cons); + if ( len2 != 0 ) + { + memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[len1], len2); + xub->prod += len2; + return PyInt_FromLong(len1 + len2); + } + } + + return PyInt_FromLong(len1); +} + +static PyObject *xu_buffer_empty(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( xub->cons == xub->prod ) + return PyInt_FromLong(1); + + return PyInt_FromLong(0); +} + +static PyObject *xu_buffer_full(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (xub->prod - xub->cons) == BUFSZ ) + return PyInt_FromLong(1); + + return PyInt_FromLong(0); +} + +static PyMethodDef xu_buffer_methods[] = { + { "peek", + (PyCFunction)xu_buffer_peek, + METH_VARARGS, + "Peek up to @max bytes from the buffer. Returns a string.\n" }, + + { "read", + (PyCFunction)xu_buffer_read, + METH_VARARGS, + "Read up to @max bytes from the buffer. Returns a string.\n" }, + + { "discard", + (PyCFunction)xu_buffer_discard, + METH_VARARGS, + "Discard up to @max bytes from the buffer. Returns number of bytes.\n" }, + + { "write", + (PyCFunction)xu_buffer_write, + METH_VARARGS, + "Write @string into buffer. Return number of bytes written.\n" }, + + { "empty", + (PyCFunction)xu_buffer_empty, + METH_VARARGS, + "Return TRUE if the buffer is empty.\n" }, + + { "full", + (PyCFunction)xu_buffer_full, + METH_VARARGS, + "Return TRUE if the buffer is full.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_buffer_type; + +static PyObject *xu_buffer_new(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + xub = PyObject_New(xu_buffer_object, &xu_buffer_type); + + if ( (xub->buf = malloc(BUFSZ)) == NULL ) + { + PyObject_Del((PyObject *)xub); + return NULL; + } + + xub->prod = xub->cons = 0; + + return (PyObject *)xub; +} + +static PyObject *xu_buffer_getattr(PyObject *obj, char *name) +{ + return Py_FindMethod(xu_buffer_methods, obj, name); +} + +static void xu_buffer_dealloc(PyObject *self) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + free(xub->buf); + PyObject_Del(self); +} + +static PyTypeObject xu_buffer_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "buffer", + sizeof(xu_buffer_object), + 0, + xu_buffer_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_buffer_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** MODULE WRAPPER *********************** + */ + +static void handle_child_death(int dummy) +{ + while ( waitpid(-1, NULL, WNOHANG) > 0 ) + continue; +} + +static PyObject *xu_autoreap(PyObject *self, PyObject *args) +{ + struct sigaction sa; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handle_child_death; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; + (void)sigaction(SIGCHLD, &sa, NULL); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef xu_methods[] = { + { "notifier", xu_notifier_new, METH_VARARGS, + "Create a new notifier." }, + { "message", xu_message_new, METH_VARARGS, + "Create a new communications message." }, + { "port", xu_port_new, METH_VARARGS, + "Create a new communications port." }, + { "buffer", xu_buffer_new, METH_VARARGS, + "Create a new ring buffer." }, + { "autoreap", xu_autoreap, METH_VARARGS, + "Ensure that zombie children are automatically reaped by the OS." }, + { NULL, NULL, 0, NULL } +}; + +PyMODINIT_FUNC initxu(void) +{ + PyObject *m, *d; + + m = Py_InitModule(XENPKG, xu_methods); + + d = PyModule_GetDict(m); + port_error = PyErr_NewException(XENPKG ".PortError", NULL, NULL); + PyDict_SetItemString(d, "PortError", port_error); +} diff --git a/tools/xu/setup.py b/tools/xu/setup.py new file mode 100644 index 0000000000..e342c78a14 --- /dev/null +++ b/tools/xu/setup.py @@ -0,0 +1,19 @@ + +from distutils.core import setup, Extension + +xu = Extension("xu", + extra_compile_args = ["-fno-strict-aliasing"], + include_dirs = ["../xc/lib", + "../../xen/include/hypervisor-ifs", + "../../linux-xen-sparse/include"], + library_dirs = ["../xc/lib"], + libraries = ["xc"], + sources = ["lib/xu.c"]) + +setup(name = "xu", + version = "1.0", + #packages = ["xend"], + #package_dir = { "xend" : "lib" }, + ext_package = "xen.ext", + ext_modules = [ xu ] + )